home *** CD-ROM | disk | FTP | other *** search
- #ifndef lintt->vector) (xlefSid = "$Id: graphics.c,v 1.24.2.2 1999/09/15 15:30:29 lhecking Exp $";
- #endif
-
- /* GNUPLOT - graphics.c */
-
- /*[
- * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
- *
- * Permission to use, copy, and distribute this software and its
- * documentation for any purpose with or without fee is hereby granted,
- * provided that the above copyright notice appear in all copies and
- * that both that copyright notice and this permission notice appear
- * in supporting documentation.
- *
- * Permission to modify the software is granted, but not the right to
- * distribute the complete modified source code. Modifications are to
- * be distributed as patches to the released version. Permission to
- * distribute binaries produced by compiling modified sources is granted,
- * provided you
- * 1. distribute the corresponding source modifications from the
- * released version in the form of a patch file along with the binaries,
- * 2. add special version identification to distinguish your version
- * in addition to the base release version number,
- * 3. provide your name and address as the primary contact for the
- * support of your modified version, and
- * 4. retain our contact information in regard to use of the base
- * software.
- * Permission to distribute the released version of the source code along
- * with corresponding source modifications in the form of a patch file is
- * granted with same provisions 2 through 4 for binary distributions.
- *
- * This software is provided "as is" without express or implied warranty
- * to the extent permitted by applicable law.
- ]*/
-
-
- #include "plot.h"
- #include "setshow.h"
-
- /* key placement is calculated in boundary, so we need file-wide variables
- * To simplify adjustments to the key, we set all these once [depends on
- * key_reverse] and use them throughout.
- */
-
- /*{{{ local and global variables */
- static int key_sample_width; /* width of line sample */
- static int key_sample_left; /* offset from x for left of line sample */
- static int key_sample_right; /* offset from x for right of line sample */
- static int key_point_offset; /* offset from x for point sample */
- static int key_text_left; /* offset from x for left-justified text */
- static int key_text_right; /* offset from x for right-justified text */
- static int key_size_left; /* size of left bit of key (text or sample, depends on key_reverse) */
- static int key_size_right; /* size of right part of key (including padding) */
-
- /* I think the following should also be static ?? */
-
- static int key_xl, key_xr, key_yt, key_yb; /* boundarys for key field */
- static int max_ptitl_len = 0; /* max length of plot-titles (keys) */
- static int ktitl_lines = 0; /* no lines in key_title (key header) */
- static int ptitl_cnt; /* count keys with len > 0 */
- static int key_cols; /* no cols of keys */
- static int key_rows, key_col_wth, yl_ref;
-
-
- /* penalty for doing tics by callback in gen_tics is need for
- * global variables to communicate with the tic routines
- * Dont need to be arrays for this
- */
- static int tic_start, tic_direction, tic_text, rotate_tics, tic_hjust, tic_vjust, tic_mirror;
-
- /* set by tic_callback - how large to draw polar radii */
- static double largest_polar_circle;
-
- /* either xformat etc or invented time format
- * index with FIRST_X_AXIS etc
- * global because used in gen_tics, which graph3d also uses
- */
- char ticfmt[8][MAX_ID_LEN+1]; /* HBB 990106: fix buffer overrun */
- int timelevel[8];
- double ticstep[8];
-
- static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
-
- static int key_entry_height; /* bigger of t->v_size, pointsize*t->v_tick */
- static int p_width, p_height; /* pointsize * { t->h_tic | t->v_tic } */
-
-
- /* there are several things on right of plot - key, y2tics and y2label
- * when working out boundary, save posn of y2label for later...
- * Same goes for x2label.
- * key posn is also stored in key_xl, and tics go at xright
- */
- static int ylabel_x, y2label_x, xlabel_y, x2label_y, title_y, time_y, time_x;
- static int ylabel_y, y2label_y, xtic_y, x2tic_y, ytic_x, y2tic_x;
- /*}}} */
-
- /*{{{ static fns and local macros */
- static void plot_impulses __PROTO((struct curve_points * plot, int yaxis_x,
- int xaxis_y));
- static void plot_lines __PROTO((struct curve_points * plot));
- static void plot_points __PROTO((struct curve_points * plot));
- static void plot_dots __PROTO((struct curve_points * plot));
- static void plot_bars __PROTO((struct curve_points * plot));
- static void plot_boxes __PROTO((struct curve_points * plot, int xaxis_y));
- static void plot_vectors __PROTO((struct curve_points * plot));
- static void plot_f_bars __PROTO((struct curve_points * plot));
- static void plot_c_bars __PROTO((struct curve_points * plot));
-
- static void edge_intersect __PROTO((struct coordinate GPHUGE * points, int i,
- double *ex, double *ey));
- static int two_edge_intersect __PROTO((struct coordinate GPHUGE * points,
- int i, double *lx, double *ly));
- static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
-
- static void plot_steps __PROTO((struct curve_points * plot)); /* JG */
- static void plot_fsteps __PROTO((struct curve_points * plot)); /* HOE */
- static void plot_histeps __PROTO((struct curve_points * plot)); /* CAC */
- static void histeps_horizontal __PROTO((int *xl, int *yl, double x1, double x2,
- double y)); /* CAC */
- static void histeps_vertical __PROTO((int *xl, int *yl, double x, double y1,
- double y2)); /* CAC */
- static void edge_intersect_steps __PROTO((struct coordinate GPHUGE * points,
- int i, double *ex, double *ey)); /* JG */
- static void edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points,
- int i, double *ex, double *ey)); /* HOE */
- static TBOOLEAN two_edge_intersect_steps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly)); /* JG */
- static TBOOLEAN two_edge_intersect_fsteps __PROTO((struct coordinate GPHUGE * points, int i, double *lx, double *ly));
-
- static double LogScale __PROTO((double coord, int is_log, double log_base_log,
- char *what, char *axis));
- static double dbl_raise __PROTO((double x, int y));
- static void boundary __PROTO((int scaling, struct curve_points * plots,
- int count));
- static double make_tics __PROTO((int axis, int guide));
-
- /* widest2d_callback keeps longest so far in here */
- static int widest_tic;
-
- static void widest2d_callback __PROTO((int axis, double place, char *text,
- struct lp_style_type grid));
- static void ytick2d_callback __PROTO((int axis, double place, char *text,
- struct lp_style_type grid));
- static void xtick2d_callback __PROTO((int axis, double place, char *text,
- struct lp_style_type grid));
- static void map_position __PROTO((struct position * pos, unsigned int *x,
- unsigned int *y, char *what));
- static void mant_exp __PROTO((double log_base, double x, int scientific,
- double *m, int *p));
- static void gprintf __PROTO((char *dest, size_t count, char *format,
- double log_base, double x));
-
- #if defined(sun386) || defined(AMIGA_SC_6_1)
- static double CheckLog __PROTO((TBOOLEAN is_log, double base_log, double x));
- #endif
-
- /* for plotting error bars
- * half the width of error bar tic mark
- */
- #define ERRORBARTIC (t->h_tic/2)
-
- /*
- * The Amiga SAS/C 6.2 compiler moans about macro envocations causing
- * multiple calls to functions. I converted these macros to inline
- * functions coping with the problem without loosing speed.
- * If your compiler supports __inline, you should add it to the
- * #ifdef directive
- * (MGR, 1993)
- */
-
- #ifdef AMIGA_SC_6_1
- GP_INLINE static TBOOLEAN i_inrange(int z, int min, int max)
- {
- return ((min < max) ? ((z >= min) && (z <= max)) : ((z >= max) && (z <= min)));
- }
-
- GP_INLINE static double f_max(double a, double b)
- {
- return (GPMAX(a, b));
- }
-
- GP_INLINE static double f_min(double a, double b)
- {
- return (GPMIN(a, b));
- }
-
- #else
- #define f_max(a,b) GPMAX((a),(b))
- #define f_min(a,b) GPMIN((a),(b))
- #define i_inrange(z,a,b) inrange((z),(a),(b))
- #endif
-
- /* True if a and b have the same sign or zero (positive or negative) */
- #define samesign(a,b) ((a) * (b) >= 0)
- /*}}} */
-
- /*{{{ more variables */
- /* Define the boundary of the plot
- * These are computed at each call to do_plot, and are constant over
- * the period of one do_plot. They actually only change when the term
- * type changes and when the 'set size' factors change.
- * - no longer true, for 'set key out' or 'set key under'. also depend
- * on tic marks and multi-line labels.
- * They are shared with graph3d.c since we want to use its draw_clip_line()
- */
- int xleft, xright, ybot, ytop;
-
-
- /* we make a local copy of the 'key' variable so that if something
- * goes wrong, we can switch it off temporarily
- */
-
- static int lkey;
-
- /* First attempt at double axes...
- * x_min etc are now accessed from a global array min_array[], max_array[]
- * put the scale factors into a similar array
- * for convenience in this first attack on double axes, just define x_min etc
- * since code already uses x_min, etc Eventually it will be done properly
- */
-
-
- extern double min_array[], max_array[];
- extern int auto_array[];
-
- extern int log_array[];
- extern double base_array[], log_base_array[];
-
- static int x_axis = FIRST_X_AXIS, y_axis = FIRST_Y_AXIS; /* current axes */
-
- static double scale[AXIS_ARRAY_SIZE]; /* scale factors for mapping for each axis */
-
- /* BODGES BEFORE I FIX IT UP */
- #define x_min min_array[x_axis]
- #define x_max max_array[x_axis]
- #define y_min min_array[y_axis]
- #define y_max max_array[y_axis]
-
- /* And the functions to map from user to terminal coordinates */
- /* maps floating point x to screen */
- #define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5)
- /* same for y */
- #define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5)
-
- /* (DFK) Watch for cancellation error near zero on axes labels */
- /* less than one hundredth of a tic mark */
- #define SIGNIF (0.01)
- #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
- #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
- /*}}} */
-
- /*{{{ CheckLog() */
- /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog
- * macro, so I write it as a function on that machine.
- *
- * Amiga SAS/C 6.2 thinks it will do too much work calling functions in
- * macro arguments twice, thus I inline theese functions. (MGR, 1993)
- * If your compiler doesn't handle those macros correctly, you should
- * also subscribe here. Even without inlining you gain speed with log plots
- */
- #if defined(sun386) || defined(AMIGA_SC_6_1)
- GP_INLINE static double CheckLog(is_log, base_log, x)
- TBOOLEAN is_log;
- double base_log;
- double x;
- {
- if (is_log)
- return (pow(base_log, x));
- else
- return (x);
- }
- #else
- /* (DFK) Use 10^x if logscale is in effect, else x */
- #define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
- #endif /* sun386 || SAS/C */
- /*}}} */
-
- /*{{{ LogScale() */
- static double LogScale(coord, is_log, log_base_log, what, axis)
- double coord; /* the value */
- TBOOLEAN is_log; /* is this axis in logscale? */
- double log_base_log; /* if so, the log of its base */
- char *what; /* what is the coord for? */
- char *axis; /* which axis is this for ("x" or "y")? */
- {
- if (is_log) {
- if (coord <= 0.0) {
- char errbuf[100]; /* place to write error message */
- (void) sprintf(errbuf, "%s has %s coord of %g; must be above 0 for log scale!",
- what, axis, coord);
- graph_error(errbuf);
- } else
- return (log(coord) / log_base_log);
- }
- return (coord);
- }
- /*}}} */
-
- /*{{{ graph_error() */
- /* handle errors during graph-plot in a consistent way */
- void graph_error(text)
- char *text;
- {
- multiplot = FALSE;
- term_end_plot();
- int_error(text, NO_CARET);
- }
-
- /*}}} */
-
-
- /*{{{ fixup_range() */
- /*
- * === SYNOPSIS ===
- *
- * This function checks whether the data and/or plot range in a given axis
- * is too small (which would cause divide-by-zero and/or infinite-loop
- * problems later on). If so,
- * - if autoscaling is in effect for this axis, we widen the range
- * - otherwise, we abort with a call to int_error() (which prints out
- * a suitable error message, then (hopefully) aborts this command and
- * returns to the command prompt or whatever).
- *
- *
- * === HISTORY AND DESIGN NOTES ===
- *
- * 1998 Oct 4, Jonathan Thornburg <jthorn@galileo.thp.univie.ac.at>
- *
- * This function used to be a (long) macro FIXUP_RANGE(AXIS, WHICH)
- * which was (identically!) defined in plot2d.c and plot3d.c . As
- * well as now being a function instead of a macro, the logic is also
- * changed: The "too small" range test no longer depends on 'set zero'
- * and is now properly scaled relative to the data magnitude.
- *
- * The key question in designing this function is the policy for just how
- * much to widen the data range by, as a function of the data magnitude.
- * This is to some extent a matter of taste. IMHO the key criterion is
- * that (at least) all of the following should (a) not infinite-loop, and
- * (b) give correct plots, regardless of the 'set zero' setting:
- * plot 6.02e23 # a huge number >> 1 / FP roundoff level
- * plot 3 # a "reasonable-sized" number
- * plot 1.23e-12 # a small number still > FP roundoff level
- * plot 1.23e-12 * sin(x) # a small function still > FP roundoff level
- * plot 1.23e-45 # a tiny number << FP roundoff level
- * plot 1.23e-45 * sin(x) # a tiny function << FP roundoff level
- * plot 0 # or (more commonly) a data file of all zeros
- * That is, IMHO gnuplot should *never* infinite-loop, and it should *never*
- * producing an incorrect or misleading plot. In contrast, the old code
- * would infinite-loop on most of these examples with 'set zero 0.0' in
- * effect, or would plot the small-amplitude sine waves as the zero function
- * with 'zero' set larger than the sine waves' amplitude.
- *
- * The current code plots all the above examples correctly and without
- * infinite looping.
- *
- *
- * === USAGE ===
- *
- * Arguments:
- * axis = (in) An integer specifying which axis (x1, x2, y1, y2, z, etc)
- * we should do our stuff for. We use this argument as an
- * index into the global arrays {min,max,auto}_array . In
- * practice this argument will typically be one of the constants
- * {FIRST,SECOND}_{X,Y,Z}_AXIS defined in plot.h.
- * axis_name --> (in) This argument should point to the character string
- * name corresponding to axis , e.g. "x", "y2", etc.
- * We use this (only) in formatting warning/error messages.
- *
- * Global Variables:
- * auto_array[axis] = (in) (defined in command.c) Bit-flags which tell
- * [in some manner I don't fully understand :=( ]
- * whether and/or how autoscaling is in effect for
- * this axis.
- * {min,max}_array = (in out) (defined in command.c) The data ranges which
- * this function manipulates.
- * c_token = (in) (defined in plot.h) Used in formatting an error message.
- *
- * Bugs:
- * - If strlen(axis_name) > strlen("%s") , we may overflow an
- * error-message buffer, which would be A Bad Thing. Caveat caller...
- */
- void fixup_range(axis, axis_name)
- int axis;
- char *axis_name;
- {
- #define MAX_AXIS_NAME_LEN 2 /* max legal strlen(axis_name) */
-
- /* These two macro definitions set the range-widening policy: */
- #define FIXUP_RANGE__WIDEN_ZERO_ABS 1.0 /* widen [0:0] by
- +/- this absolute amount */
- #define FIXUP_RANGE__WIDEN_NONZERO_REL 0.01 /* widen [nonzero:nonzero] by
- -/+ this relative amount */
-
- double dmin = min_array[axis];
- double dmax = max_array[axis];
- if (dmax - dmin == 0.0) {
- /* empty range */
- if (auto_array[axis]) {
- /* range came from autoscaling ==> widen it */
- double widen = (dmax == 0.0)
- ? FIXUP_RANGE__WIDEN_ZERO_ABS
- : FIXUP_RANGE__WIDEN_NONZERO_REL * dmax;
- fprintf(stderr,
- "Warning: empty %s range [%g:%g], ",
- axis_name, dmin, dmax);
- min_array[axis] -= widen;
- max_array[axis] += widen;
- fprintf(stderr,
- "adjusting to [%g:%g]\n",
- min_array[axis], max_array[axis]);
- } else {
- /* user has explicitly set the range */
- /* (to something empty) ==> we're in trouble */
- char msg_buffer[MAX_LINE_LEN + 1];
- sprintf(msg_buffer, "Can't plot with an empty %s range!", axis_name);
- int_error(msg_buffer, c_token); /* never returns */
- }
- }
- }
-
- /*}}} */
-
-
- /*{{{ widest2d_callback() */
- /* we determine widest tick label by getting gen_ticks to call this
- * routine with every label
- */
-
- static void widest2d_callback(axis, place, text, grid)
- int axis;
- double place;
- char *text;
- struct lp_style_type grid;
- {
- int len = label_width(text, NULL);
- if (len > widest_tic)
- widest_tic = len;
- }
- /*}}} */
-
-
- /*{{{ boundary() */
- /* borders of plotting area
- * computed once on every call to do_plot
- *
- * The order in which things is done is getting pretty critical:
- * ytop depends on title, x2label, ylabels (if no rotated text)
- * ybot depends on key, if TUNDER
- * once we have these, we can setup the y1 and y2 tics and the
- * only then can we calculate xleft and xright
- * xright depends also on key TRIGHT
- * then we can do x and x2 tics
- *
- * For set size ratio ..., everything depends on everything else...
- * not really a lot we can do about that, so we lose if the plot has to
- * be reduced vertically. But the chances are the
- * change will not be very big, so the number of tics will not
- * change dramatically.
- *
- * Margin computation redone by Dick Crawford (rccrawford@lanl.gov) 4/98
- */
-
- static void boundary(scaling, plots, count)
- TBOOLEAN scaling; /* TRUE if terminal is doing the scaling */
- struct curve_points *plots;
- int count;
- {
- int ytlen;
- int yticlin = 0, y2ticlin = 0, timelin = 0;
-
- register struct termentry *t = term;
- int key_h, key_w;
- int can_rotate = (*t->text_angle) (1);
-
- int xtic_textheight; /* height of xtic labels */
- int x2tic_textheight; /* height of x2tic labels */
- int title_textheight; /* height of title */
- int xlabel_textheight; /* height of xlabel */
- int x2label_textheight; /* height of x2label */
- int timetop_textheight; /* height of timestamp (if at top) */
- int timebot_textheight; /* height of timestamp (if at bottom) */
- int ylabel_textheight; /* height of (unrotated) ylabel */
- int y2label_textheight; /* height of (unrotated) y2label */
- int ylabel_textwidth; /* width of (rotated) ylabel */
- int y2label_textwidth; /* width of (rotated) y2label */
- int timelabel_textwidth; /* width of timestamp */
- int ytic_textwidth; /* width of ytic labels */
- int y2tic_textwidth; /* width of y2tic labels */
- int x2tic_height; /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
- int xtic_height;
- int ytic_width;
- int y2tic_width;
-
- /* figure out which rotatable items are to be rotated
- * (ylabel and y2label are rotated if possible) */
- int vertical_timelabel = can_rotate && timelabel_rotate;
- int vertical_xtics = can_rotate && rotate_xtics;
- int vertical_x2tics = can_rotate && rotate_x2tics;
- int vertical_ytics = can_rotate && rotate_ytics;
- int vertical_y2tics = can_rotate && rotate_y2tics;
-
- lkey = key; /* but we may have to disable it later */
-
- xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = 0;
-
- /*{{{ count lines in labels and tics */
- if (*title.text)
- label_width(title.text, &titlelin);
- if (*xlabel.text)
- label_width(xlabel.text, &xlablin);
- if (*x2label.text)
- label_width(x2label.text, &x2lablin);
- if (*ylabel.text)
- label_width(ylabel.text, &ylablin);
- if (*y2label.text)
- label_width(y2label.text, &y2lablin);
- if (xtics)
- label_width(xformat, &xticlin);
- if (x2tics)
- label_width(x2format, &x2ticlin);
- if (ytics)
- label_width(yformat, &yticlin);
- if (y2tics)
- label_width(y2format, &y2ticlin);
- if (*timelabel.text)
- label_width(timelabel.text, &timelin);
- /*}}} */
-
- /*{{{ preliminary ytop calculation */
-
- /* first compute heights of things to be written in the margin */
-
- /* title */
- if (titlelin)
- title_textheight = (int) ((titlelin + title.yoffset + 1) * (t->v_char));
- else
- title_textheight = 0;
-
- /* x2label */
- if (x2lablin) {
- x2label_textheight = (int) ((x2lablin + x2label.yoffset) * (t->v_char));
- if (!x2tics)
- x2label_textheight += 0.5 * t->v_char;
- } else
- x2label_textheight = 0;
-
- /* tic labels */
- if (x2tics & TICS_ON_BORDER) {
- /* ought to consider tics on axes if axis near border */
- if (vertical_x2tics) {
- /* guess at tic length, since we don't know it yet
- --- we'll fix it after the tic labels have been created */
- x2tic_textheight = (int) (5 * (t->h_char));
- } else
- x2tic_textheight = (int) ((x2ticlin) * (t->v_char));
- } else
- x2tic_textheight = 0;
-
- /* tics */
- if (!tic_in && ((x2tics & TICS_ON_BORDER) || ((xtics & TICS_MIRROR) && (xtics & TICS_ON_BORDER))))
- x2tic_height = (int) ((t->v_tic) * ticscale);
- else
- x2tic_height = 0;
-
- /* timestamp */
- if (*timelabel.text && !timelabel_bottom)
- timetop_textheight = (int) ((timelin + timelabel.yoffset + 2) * (t->v_char));
- else
- timetop_textheight = 0;
-
- /* horizontal ylabel */
- if (*ylabel.text && !can_rotate)
- ylabel_textheight = (int) ((ylablin + ylabel.yoffset) * (t->v_char));
- else
- ylabel_textheight = 0;
-
- /* horizontal y2label */
- if (*y2label.text && !can_rotate)
- y2label_textheight = (int) ((y2lablin + y2label.yoffset) * (t->v_char));
- else
- y2label_textheight = 0;
-
- /* compute ytop from the various components
- * unless tmargin is explicitly specified */
-
- ytop = (int) ((ysize + yoffset) * (t->ymax));
-
- if (tmargin < 0) {
- int top_margin = x2label_textheight + title_textheight;
- if (timetop_textheight + ylabel_textheight > top_margin)
- top_margin = timetop_textheight + ylabel_textheight;
- if (y2label_textheight > top_margin)
- top_margin = y2label_textheight;
-
- top_margin += x2tic_height + x2tic_textheight;
- /* x2tic_height and x2tic_textheight are computed as only the
- * relevant heights, but they nonetheless need a blank
- * space above them */
- if (top_margin > x2tic_height)
- top_margin += (int) (t->v_char);
-
- ytop -= top_margin;
- if (ytop == (int) (0.5 + (ysize + yoffset) * (t->ymax))) {
- /* make room for the end of rotated ytics or y2tics */
- ytop -= (int) ((t->h_char) * 2);
- }
- } else
- ytop -= (int) ((t->v_char) * tmargin);
-
- /* end of preliminary ytop calculation }}} */
-
-
- /*{{{ tentative xleft, needed for TUNDER */
- if (lmargin >= 0)
- xleft = (int) (xoffset * (t->xmax) + (t->h_char) * lmargin);
- else
- xleft = (int) (xoffset * (t->xmax) + (t->h_char) * 2);
- /*}}} */
-
-
- /*{{{ tentative xright, needed for TUNDER */
- if (rmargin >= 0)
- xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * rmargin);
- else
- xright = (int) ((xsize + xoffset) * (t->xmax) - (t->h_char) * 2);
- /*}}} */
-
-
- /*{{{ preliminary ybot calculation
- * first compute heights of labels and tics */
-
- /* tic labels */
- if (xtics & TICS_ON_BORDER) {
- /* ought to consider tics on axes if axis near border */
- if (vertical_xtics) {
- /* guess at tic length, since we don't know it yet */
- xtic_textheight = (int) ((t->h_char) * 5);
- } else
- xtic_textheight = (int) ((t->v_char) * (xticlin + 1));
- } else
- xtic_textheight = 0;
-
- /* tics */
- if (!tic_in && ((xtics & TICS_ON_BORDER) || ((x2tics & TICS_MIRROR) && (x2tics & TICS_ON_BORDER))))
- xtic_height = (int) ((t->v_tic) * ticscale);
- else
- xtic_height = 0;
-
- /* xlabel */
- if (xlablin) {
- /* offset is subtracted because if > 0, the margin is smaller */
- xlabel_textheight = (int) ((xlablin - xlabel.yoffset) * (t->v_char));
- if (!xtics)
- xlabel_textheight += 0.5 * t->v_char;
- } else
- xlabel_textheight = 0;
-
- /* timestamp */
- if (*timelabel.text && timelabel_bottom) {
- /* && !vertical_timelabel)
- * DBT 11-18-98 resize plot for vertical timelabels too !
- */
- /* offset is subtracted because if . 0, the margin is smaller */
- timebot_textheight = (int) ((timelin - timelabel.yoffset) * (t->v_char));
- } else
- timebot_textheight = 0;
-
- /* compute ybot from the various components
- * unless bmargin is explicitly specified */
-
- ybot = (int) ((t->ymax) * yoffset);
-
- if (bmargin < 0) {
- ybot += xtic_height + xtic_textheight;
- if (xlabel_textheight > 0)
- ybot += xlabel_textheight;
- if (timebot_textheight > 0)
- ybot += timebot_textheight;
- /* HBB 19990616: round to nearest integer, required to escape
- * floating point inaccuracies */
- if (ybot == (int)(0.5 + (t->ymax) * yoffset)) {
- /* make room for the end of rotated ytics or y2tics */
- ybot += (int) ((t->h_char) * 2);
- }
- } else
- ybot += (int) (bmargin * (t->v_char));
-
- /* end of preliminary ybot calculation }}} */
-
-
- #define KEY_PANIC(x) if (x) { lkey = 0; goto key_escape; }
-
- if (lkey) {
- /*{{{ essential key features */
- p_width = pointsize * t->h_tic;
- p_height = pointsize * t->v_tic;
-
- if (key_swidth >= 0)
- key_sample_width = key_swidth * (t->h_char) + p_width;
- else
- key_sample_width = 0;
-
- key_entry_height = p_height * 1.25 * key_vert_factor;
- if (key_entry_height < (t->v_char))
- key_entry_height = (t->v_char) * key_vert_factor;
-
- /* count max_len key and number keys with len > 0 */
- max_ptitl_len = find_maxl_keys(plots, count, &ptitl_cnt);
- if ((ytlen = label_width(key_title, &ktitl_lines)) > max_ptitl_len)
- max_ptitl_len = ytlen;
-
- if (key_reverse) {
- key_sample_left = -key_sample_width;
- key_sample_right = 0;
- /* if key width is being used, adjust right-justified text */
- key_text_left = t->h_char;
- key_text_right = (t->h_char) * (max_ptitl_len + 1 + key_width_fix);
- key_size_left = t->h_char - key_sample_left; /* sample left is -ve */
- key_size_right = key_text_right;
- } else {
- key_sample_left = 0;
- key_sample_right = key_sample_width;
- /* if key width is being used, adjust left-justified text */
- key_text_left = -(int) ((t->h_char) * (max_ptitl_len + 1 + key_width_fix));
- key_text_right = -(int) (t->h_char);
- key_size_left = -key_text_left;
- key_size_right = key_sample_right + t->h_char;
- }
- key_point_offset = (key_sample_left + key_sample_right) / 2;
-
- /* advance width for cols */
- key_col_wth = key_size_left + key_size_right;
-
- key_rows = ptitl_cnt;
- key_cols = 1;
-
- /* calculate rows and cols for key - if something goes wrong,
- * the tidiest way out is to set lkey = 0, and a goto
- */
-
- if (lkey == -1) {
- if (key_vpos == TUNDER) {
- /* maximise no cols, limited by label-length */
- key_cols = (int) (xright - xleft) / key_col_wth;
- KEY_PANIC(key_cols == 0);
- key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
- KEY_PANIC(key_rows == 0);
- /* now calculate actual no cols depending on no rows */
- key_cols = (int) (ptitl_cnt + key_rows - 1) / key_rows;
- KEY_PANIC(key_cols == 0);
- key_col_wth = (int) (xright - xleft) / key_cols;
- /* we divide into columns, then centre in column by considering
- * ratio of * key_left_size to key_right_size
- *
- * key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
- * do one integer division to maximise accuracy (hope we
- * don't overflow !)
- */
- key_xl = xleft - key_size_left + ((xright - xleft) * key_size_left) / (key_cols * (key_size_left + key_size_right));
- key_xr = key_xl + key_col_wth * (key_cols - 1) + key_size_left + key_size_right;
- key_yb = t->ymax * yoffset;
- key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
- ybot += key_entry_height * key_rows + (int) ((t->v_char) * (ktitl_lines + 1));
- } else {
- /* maximise no rows, limited by ytop-ybot */
- int i = (int) (ytop - ybot - (ktitl_lines + 1) * (t->v_char)) / key_entry_height;
- KEY_PANIC(i == 0);
- if (ptitl_cnt > i) {
- key_cols = (int) (ptitl_cnt + i - 1) / i;
- /* now calculate actual no rows depending on no cols */
- KEY_PANIC(key_cols == 0);
- key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
- }
- }
- /* come here if we detect a division by zero in key calculations */
- key_escape:
- ; /* ansi requires this */
- }
- /*}}} */
- }
- /*{{{ set up y and y2 tics */
- {
- /* setup_tics allows max number of tics to be specified
- * but users dont like it to change with size and font,
- * so we use value of 20, which is 3.5 behaviour.
- * Note also that if format is '', yticlin = 0, so this gives
- * division by zero.
- * int guide = (ytop-ybot)/term->v_char;
- */
- if (ytics)
- setup_tics(FIRST_Y_AXIS, &yticdef, yformat, 20 /*(int) (guide/yticlin) */ );
- if (y2tics)
- setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, 20 /*(int) (guide/y2ticlin) */ );
- }
- /*}}} */
-
-
- /*{{{ recompute xleft based on widths of ytics, ylabel etc
- unless it has been explicitly set by lmargin */
-
- /* tic labels */
- if (ytics & TICS_ON_BORDER) {
- if (vertical_ytics)
- /* HBB: we will later add some white space as part of this, so
- * reserve two more rows (one above, one below the text ...).
- * Same will be done to similar calc.'s elsewhere */
- ytic_textwidth = (int) ((t->v_char) * (yticlin + 2));
- else {
- widest_tic = 0; /* reset the global variable ... */
- /* get gen_tics to call widest2d_callback with all labels
- * the latter sets widest_tic to the length of the widest one
- * ought to consider tics on axis if axis near border...
- */
- gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
-
- ytic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
- }
- } else {
- ytic_textwidth = 0;
- }
-
- /* tics */
- if (!tic_in && ((ytics & TICS_ON_BORDER) || ((y2tics & TICS_MIRROR) && (y2tics & TICS_ON_BORDER))))
- ytic_width = (int) ((t->h_tic) * ticscale);
- else
- ytic_width = 0;
-
- /* ylabel */
- if (*ylabel.text && can_rotate) {
- ylabel_textwidth = (int) ((ylablin - ylabel.xoffset) * (t->v_char));
- if (!ytics)
- ylabel_textwidth += 0.5 * t->v_char;
- }
- /* this should get large for NEGATIVE ylabel.xoffsets DBT 11-5-98 */
- else
- ylabel_textwidth = 0;
-
- /* timestamp */
- if (*timelabel.text && vertical_timelabel)
- timelabel_textwidth = (int) ((timelin - timelabel.xoffset + 1.5) * (t->v_char));
- else
- timelabel_textwidth = 0;
-
- /* compute xleft from the various components
- * unless lmargin is explicitly specified */
-
- xleft = (int) ((t->xmax) * xoffset);
-
- if (lmargin < 0) {
- xleft += (timelabel_textwidth > ylabel_textwidth ? timelabel_textwidth : ylabel_textwidth)
- + ytic_width + ytic_textwidth;
-
- /* make sure xleft is wide enough for a negatively
- * x-offset horizontal timestamp
- */
- if (!vertical_timelabel && xleft - ytic_width - ytic_textwidth < -(int) (timelabel.xoffset * (t->h_char)))
- xleft = ytic_width + ytic_textwidth - (int) (timelabel.xoffset * (t->h_char));
- if (xleft == (int)(0.5 + (t->xmax) * xoffset)) {
- /* make room for end of xtic or x2tic label */
- xleft += (int) ((t->h_char) * 2);
- }
- /* DBT 12-3-98 extra margin just in case */
- xleft += 0.5 * t->v_char;
- } else
- xleft += (int) (lmargin * (t->h_char));
-
- /* end of xleft calculation }}} */
-
-
- /*{{{ recompute xright based on widest y2tic. y2labels, key TOUT
- unless it has been explicitly set by rmargin */
-
- /* tic labels */
- if (y2tics & TICS_ON_BORDER) {
- if (vertical_y2tics)
- y2tic_textwidth = (int) ((t->v_char) * (y2ticlin + 2));
- else {
- widest_tic = 0; /* reset the global variable ... */
- /* get gen_tics to call widest2d_callback with all labels
- * the latter sets widest_tic to the length of the widest one
- * ought to consider tics on axis if axis near border...
- */
- gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
-
- y2tic_textwidth = (int) ((t->h_char) * (widest_tic + 2));
- }
- } else {
- y2tic_textwidth = 0;
- }
-
- /* tics */
- if (!tic_in && ((y2tics & TICS_ON_BORDER) || ((ytics & TICS_MIRROR) && (ytics & TICS_ON_BORDER))))
- y2tic_width = (int) ((t->h_tic) * ticscale);
- else
- y2tic_width = 0;
-
- /* y2label */
- if (can_rotate && *y2label.text) {
- y2label_textwidth = (int) ((y2lablin + y2label.xoffset) * (t->v_char));
- if (!y2tics)
- y2label_textwidth += 0.5 * t->v_char;
- } else
- y2label_textwidth = 0;
-
- /* compute xright from the various components
- * unless rmargin is explicitly specified */
-
- xright = (int) ((t->xmax) * (xsize + xoffset));
-
- if (rmargin < 0) {
- /* xright -= y2label_textwidth + y2tic_width + y2tic_textwidth; */
- xright -= y2tic_width + y2tic_textwidth;
- if (y2label_textwidth > 0)
- xright -= y2label_textwidth;
-
- /* adjust for outside key */
- if (lkey == -1 && key_hpos == TOUT) {
- xright -= key_col_wth * key_cols;
- key_xl = xright + (int) (t->h_tic);
- }
- if (xright == (int)(0.5 + (t->xmax) * (xsize + xoffset))) {
- /* make room for end of xtic or x2tic label */
- xright -= (int) ((t->h_char) * 2);
- }
- xright -= 0.5 * t->v_char; /* DBT 12-3-98 extra margin just in case */
-
- } else
- xright -= (int) (rmargin * (t->h_char));
-
- /* end of xright calculation }}} */
-
-
- if (aspect_ratio != 0.0) {
- double current_aspect_ratio;
-
- if (aspect_ratio < 0 && (max_array[x_axis] - min_array[x_axis]) != 0.0) {
- current_aspect_ratio = -aspect_ratio * fabs((max_array[y_axis] - min_array[y_axis]) / (max_array[x_axis] - min_array[x_axis]));
- } else
- current_aspect_ratio = aspect_ratio;
-
- /*{{{ set aspect ratio if valid and sensible */
- if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
- double current = ((double) (ytop - ybot)) / ((double) (xright - xleft));
- double required = (current_aspect_ratio * (double) t->v_tic) / ((double) t->h_tic);
-
- if (current > required) {
- /* too tall */
- ytop = ybot + required * (xright - xleft);
- } else {
- xright = xleft + (ytop - ybot) / required;
- }
- }
- /*}}} */
- }
- /*{{{ set up x and x2 tics */
- /* we should base the guide on the width of the xtics, but we cannot
- * use widest_tics until tics are set up. Bit of a downer - let us
- * assume tics are 5 characters wide
- */
-
- {
- /* see equivalent code for ytics above
- * int guide = (xright - xleft) / (5*t->h_char);
- */
-
- if (xtics)
- setup_tics(FIRST_X_AXIS, &xticdef, xformat, 20 /*guide */ );
- if (x2tics)
- setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, 20 /*guide */ );
- }
- /*}}} */
-
-
- /* adjust top and bottom margins for tic label rotation */
-
- if (tmargin < 0 && x2tics & TICS_ON_BORDER && vertical_x2tics) {
- widest_tic = 0; /* reset the global variable ... */
- gen_tics(SECOND_X_AXIS, &x2ticdef, 0, 0, 0.0, widest2d_callback);
- ytop += x2tic_textheight;
- /* Now compute a new one and use that instead: */
- x2tic_textheight = (int) ((t->h_char) * (widest_tic));
- ytop -= x2tic_textheight;
- }
- if (bmargin < 0 && xtics & TICS_ON_BORDER && vertical_xtics) {
- widest_tic = 0; /* reset the global variable ... */
- gen_tics(FIRST_X_AXIS, &xticdef, 0, 0, 0.0, widest2d_callback);
- ybot -= xtic_textheight;
- xtic_textheight = (int) ((t->h_char) * widest_tic);
- ybot += xtic_textheight;
- }
- /* compute coordinates for axis labels, title et al
- * (some of these may not be used) */
-
- x2label_y = ytop + x2tic_height + x2tic_textheight + x2label_textheight;
- if (x2tic_textheight && (title_textheight || x2label_textheight))
- x2label_y += t->v_char;
-
- title_y = x2label_y + title_textheight;
-
- ylabel_y = ytop + x2tic_height + x2tic_textheight + ylabel_textheight;
-
- y2label_y = ytop + x2tic_height + x2tic_textheight + y2label_textheight;
-
- xlabel_y = ybot - xtic_height - xtic_textheight - xlabel_textheight + xlablin*(t->v_char);
- ylabel_x = xleft - ytic_width - ytic_textwidth;
- if (*ylabel.text && can_rotate)
- ylabel_x -= ylabel_textwidth;
-
- y2label_x = xright + y2tic_width + y2tic_textwidth;
- if (*y2label.text && can_rotate)
- y2label_x += y2label_textwidth - y2lablin * t->v_char;
-
- if (vertical_timelabel) {
- if (timelabel_bottom)
- time_y = xlabel_y - timebot_textheight + xlabel_textheight;
- else {
- time_y = title_y + timetop_textheight - title_textheight - x2label_textheight;
- }
- } else {
- if (timelabel_bottom)
- time_y = ybot - xtic_height - xtic_textheight - xlabel_textheight - timebot_textheight + t->v_char;
- else if (ylabel_textheight > 0)
- time_y = ylabel_y + timetop_textheight;
- else
- time_y = ytop + x2tic_height + x2tic_textheight + timetop_textheight + (int) (t->h_char);
- }
- if (vertical_timelabel)
- time_x = xleft - ytic_width - ytic_textwidth - timelabel_textwidth;
- else
- time_x = xleft - ytic_width - ytic_textwidth + (int) (timelabel.xoffset * (t->h_char));
-
- xtic_y = ybot - xtic_height - (vertical_xtics ? (int) (t->h_char) : (int) (t->v_char));
-
- x2tic_y = ytop + x2tic_height + (vertical_x2tics ? (int) (t->h_char) : x2tic_textheight);
-
- ytic_x = xleft - ytic_width - (vertical_ytics ? (ytic_textwidth - (int) t->v_char) : (int) (t->h_char));
-
- y2tic_x = xright + y2tic_width + (vertical_y2tics ? (int) (t->v_char) : (int) (t->h_char));
-
- /* restore text to horizontal [we tested rotation above] */
- (void) (*t->text_angle) (0);
-
- /* needed for map_position() below */
-
- scale[FIRST_Y_AXIS] = (ytop - ybot) / (max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
- scale[FIRST_X_AXIS] = (xright - xleft) / (max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
- scale[SECOND_Y_AXIS] = (ytop - ybot) / (max_array[SECOND_Y_AXIS] - min_array[SECOND_Y_AXIS]);
- scale[SECOND_X_AXIS] = (xright - xleft) / (max_array[SECOND_X_AXIS] - min_array[SECOND_X_AXIS]);
-
- /*{{{ calculate the window in the grid for the key */
- if (lkey == 1 || (lkey == -1 && key_vpos != TUNDER)) {
- /* calculate space for keys to prevent grid overwrite the keys */
- /* do it even if there is no grid, as do_plot will use these to position key */
- key_w = key_col_wth * key_cols;
- key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
- if (lkey == -1) {
- if (key_vpos == TTOP) {
- key_yt = (int) ytop - (t->v_tic);
- key_yb = key_yt - key_h;
- } else {
- key_yb = ybot + (t->v_tic);
- key_yt = key_yb + key_h;
- }
- if (key_hpos == TLEFT) {
- key_xl = xleft + (t->h_char); /* for Left just */
- key_xr = key_xl + key_w;
- } else if (key_hpos == TRIGHT) {
- key_xr = xright - (t->h_char); /* for Right just */
- key_xl = key_xr - key_w;
- } else { /* TOUT */
- /* do this here for do_plot() */
- /* align right first since rmargin may be manual */
- key_xr = (xsize + xoffset) * (t->xmax) - (t->h_char);
- key_xl = key_xr - key_w;
- }
- } else {
- unsigned int x, y;
- map_position(&key_user_pos, &x, &y, "key");
- key_xl = x - key_size_left;
- key_xr = key_xl + key_w;
- key_yt = y + (ktitl_lines ? t->v_char : key_entry_height) / 2;
- key_yb = key_yt - key_h;
- }
- }
- /*}}} */
-
- }
-
- /*}}} */
-
- /*{{{ dbl_raise() */
- static double dbl_raise(x, y)
- double x;
- int y;
- {
- register int i = abs(y);
- double val = 1.0;
-
- while (--i >= 0)
- val *= x;
-
- if (y < 0)
- return (1.0 / val);
- return (val);
- }
- /*}}} */
-
- /*{{{ timetic_fmt() */
- void timetic_format(axis, amin, amax)
- int axis;
- double amin, amax;
- {
- struct tm tmin, tmax;
-
- *ticfmt[axis] = 0; /* make sure we strcat to empty string */
-
- ggmtime(&tmin, (double) time_tic_just(timelevel[axis], amin));
- ggmtime(&tmax, (double) time_tic_just(timelevel[axis], amax));
- /*
- if ( tmax.tm_year == tmin.tm_year &&
- tmax.tm_mon == tmin.tm_mon &&
- tmax.tm_mday == tmin.tm_mday ) {
- */
- if (tmax.tm_year == tmin.tm_year &&
- tmax.tm_yday == tmin.tm_yday) {
- /* same day, skip date */
- if (tmax.tm_hour != tmin.tm_hour) {
- strcpy(ticfmt[axis], "%H");
- }
- if (timelevel[axis] < 3) {
- if (strlen(ticfmt[axis]))
- strcat(ticfmt[axis], ":");
- strcat(ticfmt[axis], "%M");
- }
- if (timelevel[axis] < 2) {
- strcat(ticfmt[axis], ":%S");
- }
- } else {
- if (tmax.tm_year != tmin.tm_year) {
- /* different years, include year in ticlabel */
- /* check convention, day/month or month/day */
- if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
- strcpy(ticfmt[axis], "%m/%d/%");
- } else {
- strcpy(ticfmt[axis], "%d/%m/%");
- }
- if (((int) (tmax.tm_year / 100)) != ((int) (tmin.tm_year / 100))) {
- strcat(ticfmt[axis], "Y");
- } else {
- strcat(ticfmt[axis], "y");
- }
-
- } else {
- if (strchr(timefmt, 'm') < strchr(timefmt, 'd')) {
- strcpy(ticfmt[axis], "%m/%d");
- } else {
- strcpy(ticfmt[axis], "%d/%m");
- }
- }
- if (timelevel[axis] < 4) {
- strcat(ticfmt[axis], "\n%H:%M");
- }
- }
- }
- /*}}} */
-
- /*{{{ set_tic() */
- /* the guide parameter was intended to allow the number of tics
- * to depend on the relative sizes of the plot and the font.
- * It is the approximate upper limit on number of tics allowed.
- * But it did not go down well with the users.
- * A value of 20 gives the same behaviour as 3.5, so that is
- * hardwired into the calls to here. Maybe we will restore it
- * to the automatic calculation one day
- */
-
- double set_tic(l10, guide)
- double l10;
- int guide;
- {
- double xnorm, tics, posns;
-
- int fl = (int) floor(l10);
- xnorm = pow(10.0, l10 - fl); /* approx number of decades */
-
- posns = guide / xnorm; /* approx number of tic posns per decade */
-
- if (posns > 40)
- tics = 0.05; /* eg 0, .05, .10, ... */
- else if (posns > 20)
- tics = 0.1; /* eg 0, .1, .2, ... */
- else if (posns > 10)
- tics = 0.2; /* eg 0,0.2,0.4,... */
- else if (posns > 4)
- tics = 0.5; /* 0,0.5,1, */
- else if (posns > 1)
- tics = 1; /* 0,1,2,.... */
- else if (posns > 0.5)
- tics = 2; /* 0, 2, 4, 6 */
- else
- /* getting desperate... the ceil is to make sure we
- * go over rather than under - eg plot [-10:10] x*x
- * gives a range of about 99.999 - tics=xnorm gives
- * tics at 0, 99.99 and 109.98 - BAD !
- * This way, inaccuracy the other way will round
- * up (eg 0->100.0001 => tics at 0 and 101
- * I think latter is better than former
- */
- tics = ceil(xnorm);
-
- return (tics * dbl_raise(10.0, fl));
- }
- /*}}} */
-
- /*{{{ make_tics() */
- static double make_tics(axis, guide)
- int axis, guide;
- {
- register double xr, tic, l10;
-
- xr = fabs(min_array[axis] - max_array[axis]);
-
- l10 = log10(xr);
- tic = set_tic(l10, guide);
- if (log_array[axis] && tic < 1.0)
- tic = 1.0;
- if (datatype[axis] == TIME) {
- struct tm ftm, etm;
- /* this is not fun */
- ggmtime(&ftm, (double) min_array[axis]);
- ggmtime(&etm, (double) max_array[axis]);
-
- timelevel[axis] = 0; /* seconds */
- if (tic > 20) {
- /* turn tic into units of minutes */
- tic = set_tic(log10(xr / 60.0), guide) * 60;
- timelevel[axis] = 1; /* minutes */
- }
- if (tic > 20 * 60) {
- /* turn tic into units of hours */
- tic = set_tic(log10(xr / 3600.0), guide) * 3600;
- timelevel[axis] = 2; /* hours */
- }
- if (tic > 2 * 3600) {
- /* need some tickling */
- tic = set_tic(log10(xr / (3 * 3600.0)), guide) * 3 * 3600;
- }
- if (tic > 6 * 3600) {
- /* turn tic into units of days */
- tic = set_tic(log10(xr / DAY_SEC), guide) * DAY_SEC;
- timelevel[axis] = 3; /* days */
- }
- if (tic > 3 * DAY_SEC) {
- /* turn tic into units of weeks */
- tic = set_tic(log10(xr / WEEK_SEC), guide) * WEEK_SEC;
- if (tic < WEEK_SEC) { /* force */
- tic = WEEK_SEC;
- }
- timelevel[axis] = 4; /* weeks */
- }
- if (tic > 3 * WEEK_SEC) {
- /* turn tic into units of month */
- tic = set_tic(log10(xr / MON_SEC), guide) * MON_SEC;
- if (tic < MON_SEC) { /* force */
- tic = MON_SEC;
- }
- timelevel[axis] = 5; /* month */
- }
- if (tic > 2 * MON_SEC) {
- /* turn tic into units of month */
- tic = set_tic(log10(xr / (3 * MON_SEC)), guide) * 3 * MON_SEC;
- }
- if (tic > 6 * MON_SEC) {
- /* turn tic into units of years */
- tic = set_tic(log10(xr / YEAR_SEC), guide) * YEAR_SEC;
- if (tic < (YEAR_SEC / 2)) {
- tic = YEAR_SEC / 2;
- }
- timelevel[axis] = 6; /* year */
- }
- }
- return (tic);
- }
- /*}}} */
-
-
- void do_plot(plots, pcount)
- struct curve_points *plots;
- int pcount; /* count of plots in linked list */
- {
-
- /* BODGES BEFORE I FIX IT UP */
- #define ytic ticstep[y_axis]
- #define xtic ticstep[x_axis]
-
- register struct termentry *t = term;
- register int curve;
- int axis_zero[AXIS_ARRAY_SIZE]; /* axes in terminal coords for FIRST_X_AXIS, etc */
- register struct curve_points *this_plot = NULL;
- register int xl = 0, yl = 0; /* avoid gcc -Wall warning */
- register int key_count = 0;
- /* only a Pyramid would have this many registers! */
- struct text_label *this_label;
- struct arrow_def *this_arrow;
- TBOOLEAN scaling;
- char *s, *e;
-
- /* so that macros for x_min etc pick up correct values
- * until this is done properly
- */
-
- x_axis = FIRST_X_AXIS;
- y_axis = FIRST_Y_AXIS;
-
- /* Apply the desired viewport offsets. */
- if (y_min < y_max) {
- y_min -= boff;
- y_max += toff;
- } else {
- y_max -= boff;
- y_min += toff;
- }
- if (x_min < x_max) {
- x_min -= loff;
- x_max += roff;
- } else {
- x_max -= loff;
- x_min += roff;
- }
-
- /*
- * In the beginning, this "empty range" test was for exact
- * equality, eg y_min == y_max , but that caused an infinite
- * loop once. Then the test was changed to check for being
- * within the 'zero' threshold, fabs(y_max - y_min) < zero) ,
- * but that prevented plotting data with ranges below 'zero'.
- * Now it's an absolute equality test again, since fixup_range()
- * should have widened empty ranges before we get here.
- */
- if (x_min == x_max)
- int_error("x_min should not equal x_max!", NO_CARET);
- if (y_min == y_max)
- int_error("y_min should not equal y_max!", NO_CARET);
-
- term_init(); /* may set xmax/ymax */
-
- /* compute boundary for plot (xleft, xright, ytop, ybot)
- * also calculates tics, since xtics depend on xleft
- * but xleft depends on ytics. Boundary calculations
- * depend on term->v_char etc, so terminal must be
- * initialised.
- */
-
- scaling = (*t->scale) (xsize, ysize);
-
- boundary(scaling, plots, pcount);
-
- screen_ok = FALSE;
-
- term_start_plot();
-
- /* DRAW TICS AND GRID */
- term_apply_lp_properties(&border_lp); /* border linetype */
- largest_polar_circle = 0;
-
- /* select first mapping */
- x_axis = FIRST_X_AXIS;
- y_axis = FIRST_Y_AXIS;
-
- /* label first y axis tics */
- if (ytics) {
- int axis = map_x(ZERO);
- /* set the globals ytick2d_callback() needs */
-
- if (rotate_ytics && (*t->text_angle) (1)) {
- tic_hjust = CENTRE;
- tic_vjust = JUST_BOT;
- rotate_tics = 1; /* HBB 980629 */
- ytic_x += t->v_char / 2;
- } else {
- tic_hjust = RIGHT;
- tic_vjust = JUST_CENTRE;
- rotate_tics = 0; /* HBB 980629 */
- }
-
- if (ytics & TICS_MIRROR)
- tic_mirror = xright;
- else
- tic_mirror = -1; /* no thank you */
-
- if ((ytics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
- tic_start = axis;
- tic_direction = -1;
- if (ytics & TICS_MIRROR)
- tic_mirror = tic_start;
- /* put text at boundary if axis is close to boundary */
- tic_text = (((tic_start - xleft) > (3 * t->h_char)) ? tic_start : xleft) - t->h_char;
- } else {
- tic_start = xleft;
- tic_direction = tic_in ? 1 : -1;
- tic_text = ytic_x;
- }
- /* go for it */
- gen_tics(FIRST_Y_AXIS, &yticdef,
- work_grid.l_type & (GRID_Y | GRID_MY),
- mytics, mytfreq, ytick2d_callback);
- (*t->text_angle) (0); /* reset rotation angle */
-
- }
- /* label first x axis tics */
- if (xtics) {
- int axis = map_y(ZERO);
- /* set the globals xtick2d_callback() needs */
-
- if (rotate_xtics && (*t->text_angle) (1)) {
- tic_hjust = RIGHT;
- tic_vjust = JUST_CENTRE;
- rotate_tics = 1; /* HBB 980629 */
- } else {
- tic_hjust = CENTRE;
- tic_vjust = JUST_TOP;
- rotate_tics = 0; /* HBB 980629 */
- }
-
- if (xtics & TICS_MIRROR)
- tic_mirror = ytop;
- else
- tic_mirror = -1; /* no thank you */
- if ((xtics & TICS_ON_AXIS) && !log_array[FIRST_Y_AXIS] && inrange(axis, ybot, ytop)) {
- tic_start = axis;
- tic_direction = -1;
- if (xtics & TICS_MIRROR)
- tic_mirror = tic_start;
- /* put text at boundary if axis is close to boundary */
- if (tic_start - ybot > 2 * t->v_char)
- tic_text = tic_start - ticscale * t->v_tic - t->v_char;
- else
- tic_text = ybot - t->v_char;
- } else {
- tic_start = ybot;
- tic_direction = tic_in ? 1 : -1;
- tic_text = xtic_y;
- }
- /* go for it */
- gen_tics(FIRST_X_AXIS, &xticdef,
- work_grid.l_type & (GRID_X | GRID_MX),
- mxtics, mxtfreq, xtick2d_callback);
- (*t->text_angle) (0); /* reset rotation angle */
- }
- /* select second mapping */
- x_axis = SECOND_X_AXIS;
- y_axis = SECOND_Y_AXIS;
-
- /* label second y axis tics */
- if (y2tics) {
- /* set the globalss ytick2d_callback() needs */
- int axis = map_x(ZERO);
-
- if (rotate_y2tics && (*t->text_angle) (1)) {
- tic_hjust = CENTRE;
- tic_vjust = JUST_TOP;
- rotate_tics = 1; /* HBB 980629 */
- } else {
- tic_hjust = LEFT;
- tic_vjust = JUST_CENTRE;
- rotate_tics = 0; /* HBB 980629 */
- }
-
- if (y2tics & TICS_MIRROR)
- tic_mirror = xleft;
- else
- tic_mirror = -1; /* no thank you */
- if ((y2tics & TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
- tic_start = axis;
- tic_direction = 1;
- if (y2tics & TICS_MIRROR)
- tic_mirror = tic_start;
- /* put text at boundary if axis is close to boundary */
- tic_text = (((xright - tic_start) > (3 * t->h_char)) ? tic_start : xright) + t->h_char;
- } else {
- tic_start = xright;
- tic_direction = tic_in ? -1 : 1;
- tic_text = y2tic_x;
- }
- /* go for it */
- gen_tics(SECOND_Y_AXIS, &y2ticdef,
- work_grid.l_type & (GRID_Y2 | GRID_MY2),
- my2tics, my2tfreq, ytick2d_callback);
- (*t->text_angle) (0); /* reset rotation angle */
- }
- /* label second x axis tics */
- if (x2tics) {
- int axis = map_y(ZERO);
- /* set the globals xtick2d_callback() needs */
-
- if (rotate_x2tics && (*t->text_angle) (1)) {
- tic_hjust = LEFT;
- tic_vjust = JUST_CENTRE;
- rotate_tics = 1; /* HBB 980629 */
- } else {
- tic_hjust = CENTRE;
- tic_vjust = JUST_BOT;
- rotate_tics = 0; /* HBB 980629 */
- }
-
- if (x2tics & TICS_MIRROR)
- tic_mirror = ybot;
- else
- tic_mirror = -1; /* no thank you */
- if ((x2tics & TICS_ON_AXIS) && !log_array[SECOND_Y_AXIS] && inrange(axis, ybot, ytop)) {
- tic_start = axis;
- tic_direction = 1;
- if (x2tics & TICS_MIRROR)
- tic_mirror = tic_start;
- /* put text at boundary if axis is close to boundary */
- tic_text = (((ytop - tic_start) > (2 * t->v_char)) ? tic_start : ytop) + t->v_char;
- } else {
- tic_start = ytop;
- tic_direction = tic_in ? -1 : 1;
- tic_text = x2tic_y;
- }
- /* go for it */
- gen_tics(SECOND_X_AXIS, &x2ticdef,
- work_grid.l_type & (GRID_X2 | GRID_MX2),
- mx2tics, mx2tfreq, xtick2d_callback);
- (*t->text_angle) (0); /* reset rotation angle */
- }
- /* select first mapping */
- x_axis = FIRST_X_AXIS;
- y_axis = FIRST_Y_AXIS;
-
- /* RADIAL LINES FOR POLAR GRID */
-
- /* note that draw_clip_line takes unsigneds, but (fortunately)
- * clip_line takes signeds
- */
- if (polar_grid_angle) {
- double theta = 0;
- int ox = map_x(0);
- int oy = map_y(0);
- term_apply_lp_properties(&grid_lp);
- for (theta = 0; theta < 6.29; theta += polar_grid_angle) {
- /* copy ox in case it gets moved (but it shouldn't) */
- int oox = ox;
- int ooy = oy;
- int x = map_x(largest_polar_circle * cos(theta));
- int y = map_y(largest_polar_circle * sin(theta));
- if (clip_line(&oox, &ooy, &x, &y)) {
- (*t->move) ((unsigned int) oox, (unsigned int) ooy);
- (*t->vector) ((unsigned int) x, (unsigned int) y);
- }
- }
- draw_clip_line(ox, oy,
- map_x(largest_polar_circle * cos(theta)),
- map_y(largest_polar_circle * sin(theta)));
- }
- /* DRAW AXES */
-
- /* after grid so that axes linetypes are on top */
-
- x_axis = FIRST_X_AXIS;
- y_axis = FIRST_Y_AXIS; /* chose scaling */
- axis_zero[FIRST_Y_AXIS] = map_y(0.0);
- axis_zero[FIRST_X_AXIS] = map_x(0.0);
-
- if (axis_zero[FIRST_Y_AXIS] < ybot || is_log_y)
- axis_zero[FIRST_Y_AXIS] = ybot; /* save for impulse plotting */
- else if (axis_zero[FIRST_Y_AXIS] >= ytop)
- axis_zero[FIRST_Y_AXIS] = ytop;
- else if (xzeroaxis.l_type > -3) {
- term_apply_lp_properties(&xzeroaxis);
- (*t->move) (xleft, axis_zero[FIRST_Y_AXIS]);
- (*t->vector) (xright, axis_zero[FIRST_Y_AXIS]);
- }
- if ((yzeroaxis.l_type > -3) && !is_log_x
- && axis_zero[FIRST_X_AXIS] >= xleft
- && axis_zero[FIRST_X_AXIS] < xright) {
- term_apply_lp_properties(&yzeroaxis);
- (*t->move) (axis_zero[FIRST_X_AXIS], ybot);
- (*t->vector) (axis_zero[FIRST_X_AXIS], ytop);
- }
- x_axis = SECOND_X_AXIS;
- y_axis = SECOND_Y_AXIS; /* chose scaling */
- axis_zero[SECOND_Y_AXIS] = map_y(0.0);
- axis_zero[SECOND_X_AXIS] = map_x(0.0);
-
- if (axis_zero[SECOND_Y_AXIS] < ybot || is_log_y2)
- axis_zero[SECOND_Y_AXIS] = ybot; /* save for impulse plotting */
- else if (axis_zero[SECOND_Y_AXIS] >= ytop)
- axis_zero[SECOND_Y_AXIS] = ytop;
- else if (x2zeroaxis.l_type > -3) {
- term_apply_lp_properties(&x2zeroaxis);
- (*t->move) (xleft, axis_zero[SECOND_Y_AXIS]);
- (*t->vector) (xright, axis_zero[SECOND_Y_AXIS]);
- }
- if ((y2zeroaxis.l_type > -3) && !is_log_x2 &&
- axis_zero[SECOND_X_AXIS] >= xleft &&
- axis_zero[SECOND_X_AXIS] < xright) {
- term_apply_lp_properties(&y2zeroaxis);
- (*t->move) (axis_zero[SECOND_X_AXIS], ybot);
- (*t->vector) (axis_zero[SECOND_X_AXIS], ytop);
- }
- /* DRAW PLOT BORDER */
- if (draw_border) {
- /* HBB 980609: just in case: move over to border linestyle only
- * if border is to be drawn */
- term_apply_lp_properties(&border_lp); /* border linetype */
- (*t->move) (xleft, ybot);
- if (border_south) {
- (*t->vector) (xright, ybot);
- } else {
- (*t->move) (xright, ybot);
- }
- if (border_east) {
- (*t->vector) (xright, ytop);
- } else {
- (*t->move) (xright, ytop);
- }
- if (border_north) {
- (*t->vector) (xleft, ytop);
- } else {
- (*t->move) (xleft, ytop);
- }
- if (border_west) {
- (*t->vector) (xleft, ybot);
- } else {
- (*t->move) (xleft, ybot);
- }
- }
- /* YLABEL */
- if (*ylabel.text) {
- /* we worked out x-posn in boundary() */
- if ((*t->text_angle) (1)) {
- unsigned int x = ylabel_x + (t->v_char / 2);
- unsigned int y = (ytop + ybot) / 2 + ylabel.yoffset * (t->h_char);
- write_multiline(x, y, ylabel.text, CENTRE, JUST_TOP, 1, ylabel.font);
- (*t->text_angle) (0);
- } else {
- /* really bottom just, but we know number of lines
- so we need to adjust x-posn by one line */
- unsigned int x = ylabel_x;
- unsigned int y = ylabel_y;
- write_multiline(x, y, ylabel.text, LEFT, JUST_TOP, 0, ylabel.font);
- }
- }
- /* Y2LABEL */
- if (*y2label.text) {
- /* we worked out coordinates in boundary() */
- if ((*t->text_angle) (1)) {
- unsigned int x = y2label_x + (t->v_char / 2) - 1;
- unsigned int y = (ytop + ybot) / 2 + y2label.yoffset * (t->h_char);
- write_multiline(x, y, y2label.text, CENTRE, JUST_TOP, 1, y2label.font);
- (*t->text_angle) (0);
- } else {
- /* really bottom just, but we know number of lines */
- unsigned int x = y2label_x;
- unsigned int y = y2label_y;
- write_multiline(x, y, y2label.text, RIGHT, JUST_TOP, 0, y2label.font);
- }
- }
- /* XLABEL */
- if (*xlabel.text) {
- unsigned int x = (xright + xleft) / 2 + xlabel.xoffset * (t->h_char);
- unsigned int y = xlabel_y - t->v_char / 2; /* HBB */
- write_multiline(x, y, xlabel.text, CENTRE, JUST_TOP, 0, xlabel.font);
- }
- /* PLACE TITLE */
- if (*title.text) {
- /* we worked out y-coordinate in boundary() */
- unsigned int x = (xleft + xright) / 2 + title.xoffset * t->h_char;
- unsigned int y = title_y - t->v_char / 2;
- write_multiline(x, y, title.text, CENTRE, JUST_TOP, 0, title.font);
- }
- /* X2LABEL */
- if (*x2label.text) {
- /* we worked out y-coordinate in boundary() */
- unsigned int x = (xright + xleft) / 2 + x2label.xoffset * (t->h_char);
- unsigned int y = x2label_y - t->v_char / 2 - 1;
- write_multiline(x, y, x2label.text, CENTRE, JUST_TOP, 0, x2label.font);
- }
- /* PLACE TIMEDATE */
- if (*timelabel.text) {
- /* we worked out coordinates in boundary() */
- char *str;
- time_t now;
- unsigned int x = time_x;
- unsigned int y = time_y;
- time(&now);
- /* there is probably now way to find out in advance how many
- * chars strftime() writes */
- str = gp_alloc(MAX_LINE_LEN + 1, "timelabel.text");
- strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
-
- if (timelabel_rotate && (*t->text_angle) (1)) {
- x += t->v_char / 2; /* HBB */
- if (timelabel_bottom)
- write_multiline(x, y, str, LEFT, JUST_TOP, 1, timelabel.font);
- else
- write_multiline(x, y, str, RIGHT, JUST_TOP, 1, timelabel.font);
- (*t->text_angle) (0);
- } else {
- y -= t->v_char / 2; /* HBB */
- if (timelabel_bottom)
- write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
- else
- write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
- }
- }
- /* PLACE LABELS */
- for (this_label = first_label; this_label != NULL;
- this_label = this_label->next) {
- unsigned int x, y;
- map_position(&this_label->place, &x, &y, "label");
- if (this_label->rotate && (*t->text_angle) (1)) {
- write_multiline(x, y, this_label->text, this_label->pos, JUST_TOP, 1, this_label->font);
- (*t->text_angle) (0);
- } else {
- write_multiline(x, y, this_label->text, this_label->pos, JUST_TOP, 0, this_label->font);
- }
- }
-
- /* PLACE ARROWS */
- for (this_arrow = first_arrow; this_arrow != NULL;
- this_arrow = this_arrow->next) {
- unsigned int sx, sy, ex, ey;
- map_position(&this_arrow->start, &sx, &sy, "arrow");
- map_position(&this_arrow->end, &ex, &ey, "arrow");
-
- term_apply_lp_properties(&(this_arrow->lp_properties));
- (*t->arrow) (sx, sy, ex, ey, this_arrow->head);
- }
-
- /* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
-
-
- if (lkey) { /* may have been cancelled if something went wrong */
- /* just use key_xl etc worked out in boundary() */
- xl = key_xl + key_size_left;
- yl = key_yt;
-
- if (*key_title) {
- char *ss = gp_alloc(strlen(key_title) + 2, "tmp string ss");
- strcpy(ss, key_title);
- strcat(ss, "\n");
-
- s = ss;
- yl -= t->v_char / 2;
- while ((e = (char *) strchr(s, '\n')) != NULL) {
- *e = '\0';
- if (key_just == JLEFT) {
- (*t->justify_text) (LEFT);
- (*t->put_text) (xl + key_text_left, yl, s);
- } else {
- if ((*t->justify_text) (RIGHT)) {
- (*t->put_text) (xl + key_text_right, yl, s);
- } else {
- int x = xl + key_text_right - (t->h_char) * strlen(s);
- if (key_hpos == TOUT || inrange(x, xleft, xright))
- (*t->put_text) (x, yl, s);
- }
- }
- s = ++e;
- yl -= t->v_char;
- }
- yl += t->v_char / 2;
- free(ss);
- }
- yl_ref = yl -= key_entry_height / 2; /* centralise the keys */
- key_count = 0;
-
- if (key_box.l_type > -3) {
- term_apply_lp_properties(&key_box);
- (*t->move) (key_xl, key_yb);
- (*t->vector) (key_xl, key_yt);
- (*t->vector) (key_xr, key_yt);
- (*t->vector) (key_xr, key_yb);
- (*t->vector) (key_xl, key_yb);
- /* draw a horizontal line between key title
- and first entry *//* JFi */
- (*t->move) (key_xl, key_yt - (ktitl_lines) * t->v_char); /* JFi */
- (*t->vector) (key_xr, key_yt - (ktitl_lines) * t->v_char); /* JFi */
- }
- } /* lkey */
- /* DRAW CURVES */
- this_plot = plots;
- for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
- int localkey = lkey; /* a local copy */
-
- /* set scaling for this plot's axes */
- x_axis = this_plot->x_axis;
- y_axis = this_plot->y_axis;
-
- term_apply_lp_properties(&(this_plot->lp_properties));
-
- if (this_plot->title && !*this_plot->title) {
- localkey = 0;
- } else {
- if (localkey != 0 && this_plot->title) {
- key_count++;
- if (key_just == JLEFT) {
- (*t->justify_text) (LEFT);
- (*t->put_text) (xl + key_text_left,
- yl, this_plot->title);
- } else {
- if ((*t->justify_text) (RIGHT)) {
- (*t->put_text) (xl + key_text_right,
- yl, this_plot->title);
- } else {
- int x = xl + key_text_right - (t->h_char) * strlen(this_plot->title);
- if (key_hpos == TOUT ||
- i_inrange(x, xleft, xright))
- (*t->put_text) (x, yl, this_plot->title);
- }
- }
-
- /* draw sample depending on bits set in plot_style */
- if ((this_plot->plot_style & 1) ||
- ((this_plot->plot_style & 4) && this_plot->plot_type == DATA)) { /* errors for data plots only */
- (*t->move) (xl + key_sample_left, yl);
- (*t->vector) (xl + key_sample_right, yl);
- }
- /* oops - doing the point sample now breaks postscript
- * terminal for example, which changes current line style
- * when drawing a point, but does not restore it.
- * We simply draw the point sample after plotting
- */
-
- if (this_plot->plot_type == DATA &&
- (this_plot->plot_style & 4) &&
- bar_size > 0.0) {
- (*t->move) (xl + key_sample_left, yl + ERRORBARTIC);
- (*t->vector) (xl + key_sample_left, yl - ERRORBARTIC);
- (*t->move) (xl + key_sample_right, yl + ERRORBARTIC);
- (*t->vector) (xl + key_sample_right, yl - ERRORBARTIC);
- }
- }
- }
-
- /* and now the curves, plus any special key requirements */
- /* be sure to draw all lines before drawing any points */
-
- switch (this_plot->plot_style) {
- /*{{{ IMPULSE */
- case IMPULSES:
- plot_impulses(this_plot, axis_zero[x_axis], axis_zero[y_axis]);
- break;
- /*}}} */
- /*{{{ LINES */
- case LINES:
- plot_lines(this_plot);
- break;
- /*}}} */
- /*{{{ STEPS */
- case STEPS:
- plot_steps(this_plot);
- break;
- /*}}} */
- /*{{{ FSTEPS */
- case FSTEPS:
- plot_fsteps(this_plot);
- break;
- /*}}} */
- /*{{{ HISTEPS */
- case HISTEPS:
- plot_histeps(this_plot);
- break;
- /*}}} */
- /*{{{ POINTSTYLE */
- case POINTSTYLE:
- plot_points(this_plot);
- break;
- /*}}} */
- /*{{{ LINESPOINTS */
- case LINESPOINTS:
- plot_lines(this_plot);
- plot_points(this_plot);
- break;
- /*}}} */
- /*{{{ DOTS */
- case DOTS:
- if (localkey != 0 && this_plot->title) {
- (*t->point) (xl + key_point_offset, yl, -1);
- }
- plot_dots(this_plot);
- break;
- /*}}} */
- /*{{{ YERRORBARS */
- case YERRORBARS:{
- plot_bars(this_plot);
- plot_points(this_plot);
- break;
- }
- /*}}} */
- /*{{{ XERRORBARS */
- case XERRORBARS:{
- plot_bars(this_plot);
- plot_points(this_plot);
- }
- /*}}} */
- /*{{{ XYERRORBARS */
- case XYERRORBARS:
- plot_bars(this_plot);
- plot_points(this_plot);
- break;
-
- /*}}} */
- /*{{{ BOXXYERROR */
- case BOXXYERROR:
- plot_boxes(this_plot, axis_zero[y_axis]);
- break;
- /*}}} */
- /*{{{ BOXERROR (falls through to) */
- case BOXERROR:
- plot_bars(this_plot);
- /* no break */
-
- /*}}} */
- /*{{{ BOXES */
- case BOXES:
- plot_boxes(this_plot, axis_zero[y_axis]);
- break;
- /*}}} */
- /*{{{ VECTOR */
- case VECTOR:
- plot_vectors(this_plot);
- break;
-
- /*}}} */
- /*{{{ FINANCEBARS */
- case FINANCEBARS:
- plot_f_bars(this_plot);
- break;
- /*}}} */
- /*{{{ CANDLESTICKS */
- case CANDLESTICKS:
- plot_c_bars(this_plot);
- break;
- /*}}} */
- }
-
-
- if (localkey && this_plot->title) {
- /* we deferred point sample until now */
- if (this_plot->plot_style & 2)
- (*t->point) (xl + key_point_offset, yl,
- this_plot->lp_properties.p_type);
-
- if (key_count >= key_rows) {
- yl = yl_ref;
- xl += key_col_wth;
- key_count = 0;
- } else
- yl = yl - key_entry_height;
- }
- }
-
- term_end_plot();
- }
-
-
- /* BODGES */
- #undef ytic
- #undef xtic
-
- /* plot_impulses:
- * Plot the curves in IMPULSES style
- */
-
- static void plot_impulses(plot, yaxis_x, xaxis_y)
- struct curve_points *plot;
- int yaxis_x, xaxis_y;
- {
- int i;
- int x, y;
- struct termentry *t = term;
-
- for (i = 0; i < plot->p_count; i++) {
- switch (plot->points[i].type) {
- case INRANGE:{
- x = map_x(plot->points[i].x);
- y = map_y(plot->points[i].y);
- break;
- }
- case OUTRANGE:{
- if (!inrange(plot->points[i].x, x_min, x_max))
- continue;
- x = map_x(plot->points[i].x);
- if ((y_min < y_max
- && plot->points[i].y < y_min)
- || (y_max < y_min
- && plot->points[i].y > y_min))
- y = map_y(y_min);
- else
- y = map_y(y_max);
-
- break;
- }
- default: /* just a safety */
- case UNDEFINED:{
- continue;
- }
- }
-
- if (polar)
- (*t->move) (yaxis_x, xaxis_y);
- else
- (*t->move) (x, xaxis_y);
- (*t->vector) (x, y);
- }
-
- }
-
- /* plot_lines:
- * Plot the curves in LINES style
- */
- static void plot_lines(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- int x, y; /* point in terminal coordinates */
- struct termentry *t = term;
- enum coord_type prev = UNDEFINED; /* type of previous point */
- double ex, ey; /* an edge point */
- double lx[2], ly[2]; /* two edge points */
-
- for (i = 0; i < plot->p_count; i++) {
- switch (plot->points[i].type) {
- case INRANGE:{
- x = map_x(plot->points[i].x);
- y = map_y(plot->points[i].y);
-
- if (prev == INRANGE) {
- (*t->vector) (x, y);
- } else if (prev == OUTRANGE) {
- /* from outrange to inrange */
- if (!clip_lines1) {
- (*t->move) (x, y);
- = time_tic_just edge_intersect(plot->points, i, &ex, &ey);
- (*t->move) (map_x(ex), map_y(ey));
- (*t->vector) (x, y);
- }
- } else { /* prev == UNDEFINED */
- (*t->move) (x, y);
- (*t->vector) (x, y);
- }
-
- break;
- }
- case OUTRANGE:{
- if (prev == INRANGE) {
- /* from inrange to outrange */
- if (clip_lines1) {
- edge_intersect(plot->points, i, &ex, &ey);
- (*t->vector) (map_x(ex), map_y(ey));
- }
- } else if (prev == OUTRANGE) {
- /* from outrange to outrange */
- if (clip_lines2) {
- if (two_edge_intersect(plot->points, i, lx, ly)) {
- (*t->move) (map_x(lx[0]), map_y(ly[0]));
- (*t->vector) (map_x(lx[1]), map_y(ly[1]));
- }
- }
- }
- break;
- }
- default: /* just a safety */
- case UNDEFINED:{
- break;
- }
- }
- prev = plot->points[i].type;
- }
- }
-
- /* XXX - JG */
- /* plot_steps:
- * Plot the curves in STEPS style
- */
- static void plot_steps(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- int x, y; /* point in terminal coordinates */
- struct termentry *t = term;
- enum coord_type prev = UNDEFINED; /* type of previous point */
- double ex, ey; /* an edge point */
- double lx[2], ly[2]; /* two edge points */
- int yprev = 0; /* previous point coordinates */
-
- for (i = 0; i < plot->p_count; i++) {
- switch (plot->points[i].type) {
- case INRANGE:{
- x = map_x(plot->points[i].x);
- y = map_y(plot->points[i].y);
-
- if (prev == INRANGE) {
- (*t->vector) (x, yprev);
- (*t->vector) (x, y);
- } else if (prev == OUTRANGE) {
- /* from outrange to inrange */
- if (!clip_lines1) {
- (*t->move) (x, y);
- } else { /* find edge intersection */
- edge_intersect_steps(plot->points, i, &ex, &ey);
- (*t->move) (map_x(ex), map_y(ey));
- (*t->vector) (x, map_y(ey));
- (*t->vector) (x, y);
- }
- } else { /* prev == UNDEFINED */
- (*t->move) (x, y);
- (*t->vector) (x, y);
- }
- yprev = y;
- break;
- }
- case OUTRANGE:{
- if (prev == INRANGE) {
- /* from inrange to outrange */
- if (clip_lines1) {
- edge_intersect_steps(plot->points, i, &ex, &ey);
- (*t->vector) (map_x(ex), yprev);
- (*t->vector) (map_x(ex), map_y(ey));
- }
- } else if (prev == OUTRANGE) {
- /* from outrange to outrange */
- if (clip_lines2) {
- if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
- (*t->move) (map_x(lx[0]), map_y(ly[0]));
- (*t->vector) (map_x(lx[1]), map_y(ly[0]));
- (*t->vector) (map_x(lx[1]), map_y(ly[1]));
- }
- }
- }
- break;
- }
- default: /* just a safety */
- case UNDEFINED:{
- break;
- }
- }
- prev = plot->points[i].type;
- }
- }
- /* XXX - HOE */
- /* plot_fsteps:
- * Plot the curves in STEPS style by step on forward yvalue
- */
- static void plot_fsteps(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- int x, y; /* point in terminal coordinates */
- struct termentry *t = term;
- enum coord_type prev = UNDEFINED; /* type of previous point */
- double ex, ey; /* an edge point */
- double lx[2], ly[2]; /* two edge points */
- int xprev = 0; /* previous point coordinates */
-
- for (i = 0; i < plot->p_count; i++) {
- switch (plot->points[i].type) {
- case INRANGE:{
- x = map_x(plot->points[i].x);
- y = map_y(plot->points[i].y);
-
- if (prev == INRANGE) {
- (*t->vector) (xprev, y);
- (*t->vector) (x, y);
- } else if (prev == OUTRANGE) {
- /* from outrange to inrange */
- if (!clip_lines1) {
- (*t->move) (x, y);
- } else { /* find edge intersection */
- edge_intersect_fsteps(plot->points, i, &ex, &ey);
- (*t->move) (map_x(ex), map_y(ey));
- (*t->vector) (map_x(ex), y);
- (*t->vector) (x, y);
- }
- } else { /* prev == UNDEFINED */
- (*t->move) (x, y);
- (*t->vector) (x, y);
- }
- xprev = x;
- break;
- }
- case OUTRANGE:{
- if (prev == INRANGE) {
- /* from inrange to outrange */
- if (clip_lines1) {
- edge_intersect_fsteps(plot->points, i, &ex, &ey);
- (*t->vector) (xprev, map_y(ey));
- (*t->vector) (map_x(ex), map_y(ey));
- }
- } else if (prev == OUTRANGE) {
- /* from outrange to outrange */
- if (clip_lines2) {
- if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
- (*t->move) (map_x(lx[0]), map_y(ly[0]));
- (*t->vector) (map_x(lx[0]), map_y(ly[1]));
- (*t->vector) (map_x(lx[1]), map_y(ly[1]));
- }
- }
- }
- break;
- }
- default: /* just a safety */
- case UNDEFINED:{
- break;
- }
- }
- prev = plot->points[i].type;
- }
- }
-
- /* CAC */
- /* plot_histeps:
- * Plot the curves in HISTEPS style
- */
- static void plot_histeps(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- int hold, bigi; /* indices for sorting */
- int xl, yl; /* cursor position in terminal coordinates */
- struct termentry *t = term;
- double x, y, xn, yn; /* point position */
- int *gl, goodcount; /* array to hold list of valid points */
-
- /* preliminary count of points inside array */
- goodcount = 0;
- for (i = 0; i < plot->p_count; i++)
- if (plot->points[i].type == INRANGE ||
- plot->points[i].type == OUTRANGE)
- ++goodcount;
- if (goodcount < 2)
- return; /* cannot plot less than 2 points */
-
- gl = (int *) gp_alloc(goodcount * sizeof(int), "histeps valid point mapping");
- if (gl == NULL)
- return;
-
- /* fill gl array with indexes of valid (non-undefined) points. */
- goodcount = 0;
- for (i = 0; i < plot->p_count; i++)
- if (plot->points[i].type == INRANGE ||
- plot->points[i].type == OUTRANGE) {
- gl[goodcount] = i;
- ++goodcount;
- }
- /* sort the data */
- for (bigi = i = 1; i < goodcount;) {
- if (plot->points[gl[i]].x < plot->points[gl[i - 1]].x) {
- hold = gl[i];
- gl[i] = gl[i - 1];
- gl[i - 1] = hold;
- if (i > 1) {
- i--;
- continue;
- }
- }
- i = ++bigi;
- }
-
- x = (3.0 * plot->points[gl[0]].x - plot->points[gl[1]].x) / 2.0;
- y = 0.0;
-
- xl = map_x(x);
- yl = map_y(y);
- (*t->move) (xl, yl);
-
- for (i = 0; i < goodcount - 1; i++) { /* loop over all points except last */
-
- yn = plot->points[gl[i]].y;
- xn = (plot->points[gl[i]].x + plot->points[gl[i + 1]].x) / 2.0;
- histeps_vertical(&xl, &yl, x, y, yn);
- histeps_horizontal(&xl, &yl, x, xn, yn);
-
- x = xn;
- y = yn;
- }
-
- yn = plot->points[gl[i]].y;
- xn = (3.0 * plot->points[gl[i]].x - plot->points[gl[i - 1]].x) / 2.0;
- histeps_vertical(&xl, &yl, x, y, yn);
- histeps_horizontal(&xl, &yl, x, xn, yn);
- histeps_vertical(&xl, &yl, xn, yn, 0.0);
-
- free(gl);
- }
- /* CAC
- * Draw vertical line for the histeps routine.
- * Performs clipping.
- */
- static void histeps_vertical(xl, yl, x, y1, y2)
- int *xl, *yl; /* keeps track of "cursor" position */
- double x, y1, y2; /* coordinates of vertical line */
- {
- struct termentry *t = term;
- /* global x_min, x_max, y_min, y_max */
- int xm, y1m, y2m;
-
- if ((y1 < y_min && y2 < y_min) ||
- (y1 > y_max && y2 > y_max) ||
- x < x_min ||
- x > x_max)
- return;
-
- if (y1 < y_min)
- y1 = y_min;
- if (y1 > y_max)
- y1 = y_max;
- if (y2 < y_min)
- y2 = y_min;
- if (y2 > y_max)
- y2 = y_max;
-
- xm = map_x(x);
- y1m = map_y(y1);
- y2m = map_y(y2);
-
- if (y1m != *yl || xm != *xl)
- (*t->move) (xm, y1m);
- (*t->vector) (xm, y2m);
- *xl = xm;
- *yl = y2m;
-
- return;
- }
- /* CAC
- * Draw horizontal line for the histeps routine.
- * Performs clipping.
- */
- static void histeps_horizontal(xl, yl, x1, x2, y)
- int *xl, *yl; /* keeps track of "cursor" position */
- double x1, x2, y; /* coordinates of vertical line */
- {
- struct termentry *t = term;
- /* global x_min, x_max, y_min, y_max */
- int x1m, x2m, ym;
-
- if ((x1 < x_min && x2 < x_min) ||
- (x1 > x_max && x2 > x_max) ||
- y < y_min ||
- y > y_max)
- return;
-
- if (x1 < x_min)
- x1 = x_min;
- if (x1 > x_max)
- x1 = x_max;
- if (x2 < x_min)
- x2 = x_min;
- if (x2 > x_max)
- x2 = x_max;
-
- ym = map_y(y);
- x1m = map_x(x1);
- x2m = map_x(x2);
-
- if (x1m != *xl || ym != *yl)
- (*t->move) (x1m, ym);
- (*t->vector) (x2m, ym);
- *xl = x2m;
- *yl = ym;
-
- return;
- }
-
-
- /* plot_bars:
- * Plot the curves in ERRORBARS style
- * we just plot the bars; the points are plotted in plot_points
- */
- static void plot_bars(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- struct termentry *t = term;
- double x, y; /* position of the bar */
- double ylow, yhigh; /* the ends of the bars */
- double xlow, xhigh;
- double x1, y1, x2, y2, slope; /* parameters for polar error bars */
- unsigned int xM, ylowM, yhighM; /* the mapped version of above */
- unsigned int yM, xlowM, xhighM;
- TBOOLEAN low_inrange, high_inrange;
- int tic = ERRORBARTIC;
-
- /* Limitation: no boxes with x errorbars */
-
- if ((plot->plot_style == YERRORBARS) || (plot->plot_style == XYERRORBARS) ||
- (plot->plot_style == BOXERROR)) {
- /* Draw the vertical part of the bar */
- for (i = 0; i < plot->p_count; i++) {
- /* undefined points don't count */
- if (plot->points[i].type == UNDEFINED)
- continue;
-
- /* check to see if in xrange */
- x = plot->points[i].x;
- if (!inrange(x, x_min, x_max))
- continue;
- xM = map_x(x);
-
- /* check to see if in yrange */
- y = plot->points[i].y;
- if (!inrange(y, y_min, y_max))
- continue;
- yM = map_y(y);
-
- /* find low and high points of bar, and check yrange */
- yhigh = plot->points[i].yhigh;
- ylow = plot->points[i].ylow;
-
- high_inrange = inrange(yhigh, y_min, y_max);
- low_inrange = inrange(ylow, y_min, y_max);
-
- /* compute the plot position of yhigh */
- if (high_inrange)
- yhighM = map_y(yhigh);
- else if (samesign(yhigh - y_max, y_max - y_min))
- yhighM = map_y(y_max);
- else
- yhighM = map_y(y_min);
-
- /* compute the plot position of ylow */
- if (low_inrange)
- ylowM = map_y(ylow);
- else if (samesign(ylow - y_max, y_max - y_min))
- ylowM = map_y(y_max);
- else
- ylowM = map_y(y_min);
-
- if (!high_inrange && !low_inrange && ylowM == yhighM)
- /* both out of range on the same side */
- continue;
-
- /* find low and high points of bar, and check xrange */
- xhigh = plot->points[i].xhigh;
- xlow = plot->points[i].xlow;
-
- high_inrange = inrange(xhigh, x_min, x_max);
- low_inrange = inrange(xlow, x_min, x_max);
-
- /* compute the plot position of xhigh */
- if (high_inrange)
- xhighM = map_x(xhigh);
- else if (samesign(xhigh - x_max, x_max - x_min))
- xhighM = map_x(x_max);
- else
- xhighM = map_x(x_min);
-
- /* compute the plot position of xlow */
- if (low_inrange)
- xlowM = map_x(xlow);
- else if (samesign(xlow - x_max, x_max - x_min))
- xlowM = map_x(x_max);
- else
- xlowM = map_x(x_min);
-
- if (!high_inrange && !low_inrange && xlowM == xhighM)
- /* both out of range on the same side */
- continue;
-
- /* by here everything has been mapped */
- if (!polar) {
- /* HBB 981130: use Igor's routine *only* for polar errorbars */
- (*t->move) (xM, ylowM);
- /* draw the main bar */
- (*t->vector) (xM, yhighM);
- if (bar_size > 0.0) {
- /* draw the bottom tic */
- (*t->move) ((unsigned int) (xM - bar_size * tic), ylowM);
- (*t->vector) ((unsigned int) (xM + bar_size * tic), ylowM);
- /* draw the top tic */
- (*t->move) ((unsigned int) (xM - bar_size * tic), yhighM);
- (*t->vector) ((unsigned int) (xM + bar_size * tic), yhighM);
- }
- } else {
- /* HBB 981130: see above */
- /* The above has been replaced by Igor inorder to get errorbars
- coming out in polar mode AND to stop the bar from going
- through the symbol */
- if ((xhighM - xlowM) * (xhighM - xlowM) + (yhighM - ylowM) * (yhighM - ylowM)
- > pointsize * tic * pointsize * tic * 4.5) {
- /* Only plot the error bar if it is bigger than the
- * symbol */
- /* The factor of 4.5 should strictly be 4.0, but it looks
- * better to drop the error bar if it is only slightly
- * bigger than the symbol, Igor. */
- if (xlowM == xhighM) {
- (*t->move) (xM, ylowM);
- /* draw the main bar to the symbol end */
- (*t->vector) (xM, (unsigned int) (yM - pointsize * tic));
- (*t->move) (xM, (unsigned int) (yM + pointsize * tic));
- /* draw the other part of the main bar */
- (*t->vector) (xM, yhighM);
- } else {
- (*t->move) (xlowM, ylowM);
- /* draw the main bar in polar mode. Note that here
- * the bar is drawn through the symbol. I tried to
- * fix this, but got into trouble with the two bars
- * (on either side of symbol) not being perfectly
- * parallel due to mapping considerations. Igor
- */
- (*t->vector) (xhighM, yhighM);
- }
- if (bar_size > 0.0) {
- /* The following attempts to ensure that the tics
- * are perpendicular to the error bar, Igor. */
- /*perpendicular to the main bar */
- slope = (xlowM * 1.0 - xhighM * 1.0) / (yhighM * 1.0 - ylowM * 1.0 + 1e-10);
- x1 = xlowM + bar_size * tic / sqrt(1.0 + slope * slope);
- x2 = xlowM - bar_size * tic / sqrt(1.0 + slope * slope);
- y1 = slope * (x1 - xlowM) + ylowM;
- y2 = slope * (x2 - xlowM) + ylowM;
-
- /* draw the bottom tic */
- (*t->move) ((unsigned int) x1, (unsigned int) y1);
- (*t->vector) ((unsigned int) x2, (unsigned int) y2);
-
- x1 = xhighM + bar_size * tic / sqrt(1.0 + slope * slope);
- x2 = xhighM - bar_size * tic / sqrt(1.0 + slope * slope);
- y1 = slope * (x1 - xhighM) + yhighM;
- y2 = slope * (x2 - xhighM) + yhighM;
- /* draw the top tic */
- (*t->move) ((unsigned int) x1, (unsigned int) y1);
- (*t->vector) ((unsigned int) x2, (unsigned int) y2);
- } /* if error bar is bigger than symbol */
- }
- } /* HBB 981130: see above */
- } /* for loop */
- } /* if yerrorbars OR xyerrorbars */
- if ((plot->plot_style == XERRORBARS) || (plot->plot_style == XYERRORBARS)) {
-
- /* Draw the horizontal part of the bar */
- for (i = 0; i < plot->p_count; i++) {
- /* undefined points don't count */
- if (plot->points[i].type == UNDEFINED)
- continue;
-
- /* check to see if in yrange */
- y = plot->points[i].y;
- if (!inrange(y, y_min, y_max))
- continue;
- yM = map_y(y);
-
- /* find low and high points of bar, and check xrange */
- xhigh = plot->points[i].xhigh;
- xlow = plot->points[i].xlow;
-
- high_inrange = inrange(xhigh, x_min, x_max);
- low_inrange = inrange(xlow, x_min, x_max);
-
- /* compute the plot position of xhigh */
- if (high_inrange)
- xhighM = map_x(xhigh);
- else if (samesign(xhigh - x_max, x_max - x_min))
- xhighM = map_x(x_max);
- else
- xhighM = map_x(x_min);
-
- /* compute the plot position of xlow */
- if (low_inrange)
- xlowM = map_x(xlow);
- else if (samesign(xlow - x_max, x_max - x_min))
- xlowM = map_x(x_max);
- else
- xlowM = map_x(x_min);
-
- if (!high_inrange && !low_inrange && xlowM == xhighM)
- /* both out of range on the same side */
- continue;
-
- /* by here everything has been mapped */
- (*t->move) (xlowM, yM);
- (*t->vector) (xhighM, yM); /* draw the main bar */
- if (bar_size > 0.0) {
- (*t->move) (xlowM, (unsigned int) (yM - bar_size * tic)); /* draw the left tic */
- (*t->vector) (xlowM, (unsigned int) (yM + bar_size * tic));
- (*t->move) (xhighM, (unsigned int) (yM - bar_size * tic)); /* draw the right tic */
- (*t->vector) (xhighM, (unsigned int) (yM + bar_size * tic));
- }
- } /* for loop */
- } /* if xerrorbars OR xyerrorbars */
- }
-
- /* plot_boxes:
- * Plot the curves in BOXES style
- */
- static void plot_boxes(plot, xaxis_y)
- struct curve_points *plot;
- int xaxis_y;
- {
- int i; /* point index */
- int xl, xr, yt; /* point in terminal coordinates */
- double dxl, dxr, dyt;
- struct termentry *t = term;
- enum coord_type prev = UNDEFINED; /* type of previous point */
- TBOOLEAN boxxy = (plot->plot_style == BOXXYERROR);
-
- for (i = 0; i < plot->p_count; i++) {
-
- switch (plot->points[i].type) {
- case OUTRANGE:
- case INRANGE:{
- if (plot->points[i].z < 0.0) {
- /* need to auto-calc width */
- /* ASSERT(boxwidth <= 0.0); - else graphics.c
- * provides width */
-
- /* calculate width */
- if (prev != UNDEFINED)
- dxl = (plot->points[i - 1].x - plot->points[i].x) / 2.0;
- else
- dxl = 0.0;
-
- if (i < plot->p_count - 1) {
- if (plot->points[i + 1].type != UNDEFINED)
- dxr = (plot->points[i + 1].x - plot->points[i].x) / 2.0;
- else
- dxr = -dxl;
- } else {
- dxr = -dxl;
- }
-
- if (prev == UNDEFINED)
- dxl = -dxr;
-
- dxl = plot->points[i].x + dxl;
- dxr = plot->points[i].x + dxr;
- } else { /* z >= 0 */
- dxr = plot->points[i].xhigh;
- dxl = plot->points[i].xlow;
- }
-
- if (boxxy) {
- dyt = plot->points[i].yhigh;
- xaxis_y = map_y(plot->points[i].ylow);
- } else {
- dyt = plot->points[i].y;
- }
-
- /* clip to border */
- if ((y_min < y_max && dyt < y_min)
- || (y_max < y_min && dyt > y_min))
- dyt = y_min;
- if ((y_min < y_max && dyt > y_max)
- || (y_max < y_min && dyt < y_max))
- dyt = y_max;
- if ((x_min < x_max && dxr < x_min)
- || (x_max < x_min && dxr > x_min))
- dxr = x_min;
- if ((x_min < x_max && dxr > x_max)
- || (x_max < x_min && dxr < x_max))
- dxr = x_max;
- if ((x_min < x_max && dxl < x_min)
- || (x_max < x_min && dxl > x_min))
- dxl = x_min;
- if ((x_min < x_max && dxl > x_max)
- || (x_max < x_min && dxl < x_max))
- dxl = x_max;
-
- xl = map_x(dxl);
- xr = map_x(dxr);
- yt = map_y(dyt);
-
- (*t->move) (xl, xaxis_y);
- (*t->vector) (xl, yt);
- (*t->vector) (xr, yt);
- (*t->vector) (xr, xaxis_y);
- (*t->vector) (xl, xaxis_y);
- break;
- } /* case OUTRANGE, INRANGE */
-
- default: /* just a safety */
- case UNDEFINED:{
- break;
- }
-
- } /* switch point-type */
-
- prev = plot->points[i].type;
-
- } /*loop */
- }
-
-
-
- /* plot_points:
- * Plot the curves in POINTSTYLE style
- */
- static void plot_points(plot)
- struct curve_points *plot;
- {
- int i;
- int x, y;
- struct termentry *t = term;
-
- for (i = 0; i < plot->p_count; i++) {
- if (plot->points[i].type == INRANGE) {
- x = map_x(plot->points[i].x);
- y = map_y(plot->points[i].y);
- /* do clipping if necessary */
- if (!clip_points ||
- (x >= xleft + p_width && y >= ybot + p_height
- && x <= xright - p_width && y <= ytop - p_height))
- (*t->point) (x, y, plot->lp_properties.p_type);
- }
- }
- }
-
- /* plot_dots:
- * Plot the curves in DOTS style
- */
- static void plot_dots(plot)
- struct curve_points *plot;
- {
- int i;
- int x, y;
- struct termentry *t = term;
-
- for (i = 0; i < plot->p_count; i++) {
- if (plot->points[i].type == INRANGE) {
- x = map_x(plot->points[i].x);
- y = map_y(plot->points[i].y);
- /* point type -1 is a dot */
- (*t->point) (x, y, -1);
- }
- }
- }
-
- /* plot_vectors:
- * Plot the curves in VECTORS style
- */
- static void plot_vectors(plot)
- struct curve_points *plot;
- {
- int i;
- int x1, y1, x2, y2;
- struct termentry *t = term;
-
- for (i = 0; i < plot->p_count; i++) {
- if (plot->points[i].type == INRANGE) {
- x1 = map_x(plot->points[i].xlow);
- y1 = map_y(plot->points[i].ylow);
- x2 = map_x(plot->points[i].xhigh);
- y2 = map_y(plot->points[i].yhigh);
- (*t->arrow) (x1, y1, x2, y2, TRUE);
- }
- }
- }
-
-
- /* plot_f_bars() - finance bars */
- static void plot_f_bars(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- struct termentry *t = term;
- double x; /* position of the bar */
- double ylow, yhigh, yclose, yopen; /* the ends of the bars */
- unsigned int xM, ylowM, yhighM; /* the mapped version of above */
- TBOOLEAN low_inrange, high_inrange;
- int tic = ERRORBARTIC / 2;
-
- for (i = 0; i < plot->p_count; i++) {
- /* undefined points don't count */
- if (plot->points[i].type == UNDEFINED)
- continue;
-
- /* check to see if in xrange */
- x = plot->points[i].x;
- if (!inrange(x, x_min, x_max))
- continue;
- xM = map_x(x);
-
- /* find low and high points of bar, and check yrange */
- yhigh = plot->points[i].yhigh;
- ylow = plot->points[i].ylow;
- yclose = plot->points[i].z;
- yopen = plot->points[i].y;
-
- high_inrange = inrange(yhigh, y_min, y_max);
- low_inrange = inrange(ylow, y_min, y_max);
-
- /* compute the plot position of yhigh */
- if (high_inrange)
- yhighM = map_y(yhigh);
- else if (samesign(yhigh - y_max, y_max - y_min))
- yhighM = map_y(y_max);
- else
- yhighM = map_y(y_min);
-
- /* compute the plot position of ylow */
- if (low_inrange)
- ylowM = map_y(ylow);
- else if (samesign(ylow - y_max, y_max - y_min))
- ylowM = map_y(y_max);
- else
- ylowM = map_y(y_min);
-
- if (!high_inrange && !low_inrange && ylowM == yhighM)
- /* both out of range on the same side */
- continue;
-
- /* by here everything has been mapped */
- (*t->move) (xM, ylowM);
- (*t->vector) (xM, yhighM); /* draw the main bar */
- /* draw the open tic */
- (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
- (*t->vector) (xM, map_y(yopen));
- /* draw the close tic */
- (*t->move) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
- (*t->vector) (xM, map_y(yclose));
- }
- }
-
-
- /* plot_c_bars:
- * Plot the curves in CANDLESTICSK style
- * we just plot the bars; the points are not plotted
- */
- static void plot_c_bars(plot)
- struct curve_points *plot;
- {
- int i; /* point index */
- struct termentry *t = term;
- double x; /* position of the bar */
- double ylow, yhigh, yclose, yopen; /* the ends of the bars */
- unsigned int xM, ylowM, yhighM; /* the mapped version of above */
- TBOOLEAN low_inrange, high_inrange;
- int tic = ERRORBARTIC / 2;
-
- for (i = 0; i < plot->p_count; i++) {
- /* undefined points don't count */
- if (plot->points[i].type == UNDEFINED)
- continue;
-
- /* check to see if in xrange */
- x = plot->points[i].x;
- if (!inrange(x, x_min, x_max))
- continue;
- xM = map_x(x);
-
- /* find low and high points of bar, and check yrange */
- yhigh = plot->points[i].yhigh;
- ylow = plot->points[i].ylow;
- yclose = plot->points[i].z;
- yopen = plot->points[i].y;
-
- high_inrange = inrange(yhigh, y_min, y_max);
- low_inrange = inrange(ylow, y_min, y_max);
-
- /* compute the plot position of yhigh */
- if (high_inrange)
- yhighM = map_y(yhigh);
- else if (samesign(yhigh - y_max, y_max - y_min))
- yhighM = map_y(y_max);
- else
- yhighM = map_y(y_min);
-
- /* compute the plot position of ylow */
- if (low_inrange)
- ylowM = map_y(ylow);
- else if (samesign(ylow - y_max, y_max - y_min))
- ylowM = map_y(y_max);
- else
- ylowM = map_y(y_min);
-
- if (!high_inrange && !low_inrange && ylowM == yhighM)
- /* both out of range on the same side */
- continue;
-
- /* by here everything has been mapped */
- if (yopen <= yclose) {
- (*t->move) (xM, ylowM);
- (*t->vector) (xM, map_y(yopen)); /* draw the lower bar */
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
- (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
- (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
- (*t->move) (xM, map_y(yclose)); /* draw the close tic */
- (*t->vector) (xM, yhighM);
- } else {
- (*t->move) (xM, ylowM);
- (*t->vector) (xM, yhighM);
- /* draw the open tic */
- (*t->move) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yopen));
- (*t->vector) ((unsigned int) (xM + bar_size * tic), map_y(yclose));
- (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yclose));
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM - bar_size * tic), map_y(yopen));
- /* draw the close tic */
- (*t->move) ((unsigned int) (xM - bar_size * tic / 2), map_y(yclose));
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM - bar_size * tic / 2), map_y(yopen));
- /* draw the close tic */
- (*t->move) ((unsigned int) (xM + bar_size * tic / 2), map_y(yclose));
- /* draw the open tic */
- (*t->vector) ((unsigned int) (xM + bar_size * tic / 2), map_y(yopen));
- }
-
- }
- }
-
- /* single edge intersection algorithm */
- /* Given two points, one inside and one outside the plot, return
- * the point where an edge of the plot intersects the line segment defined
- * by the two points.
- */
- static void edge_intersect(points, i, ex, ey)
- struct coordinate GPHUGE *points; /* the points array */
- int i; /* line segment from point i-1 to point i */
- double *ex, *ey; /* the point where it crosses an edge */
- {
- /* global x_min, x_max, y_min, x_max */
- double ix = points[i - 1].x;
- double iy = points[i - 1].y;
- double ox = points[i].x;
- double oy = points[i].y;
- double x, y; /* possible intersection point */
-
- if (points[i].type == INRANGE) {
- /* swap points around so that ix/ix/iz are INRANGE and
- * ox/oy/oz are OUTRANGE
- */
- x = ix;
- ix = ox;
- ox = x;
- y = iy;
- iy = oy;
- oy = y;
- }
- /* nasty degenerate cases, effectively drawing to an infinity point (?)
- * cope with them here, so don't process them as a "real" OUTRANGE point
- *
- * If more than one coord is -VERYLARGE, then can't ratio the "infinities"
- * so drop out by returning the INRANGE point.
- *
- * Obviously, only need to test the OUTRANGE point (coordinates) */
- if (ox == -VERYLARGE || oy == -VERYLARGE) {
- *ex = ix;
- *ey = iy;
-
- if (ox == -VERYLARGE) {
- /* can't get a direction to draw line, so simply
- * return INRANGE point */
- if (oy == -VERYLARGE)
- return;
-
- *ex = x_min;
- return;
- }
- /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
- *ey = y_min;
- return;
- }
- /*
- * Can't have case (ix == ox && iy == oy) as one point
- * is INRANGE and one point is OUTRANGE.
- */
- if (iy == oy) {
- /* horizontal line */
- /* assume inrange(iy, y_min, y_max) */
- *ey = iy; /* == oy */
-
- if (inrange(x_max, ix, ox))
- *ex = x_max;
- else if (inrange(x_min, ix, ox))
- *ex = x_min;
- else {
- graph_error("error in edge_intersect");
- }
- return;
- } else if (ix == ox) {
- /* vertical line */
- /* assume inrange(ix, x_min, x_max) */
- *ex = ix; /* == ox */
-
- if (inrange(y_max, iy, oy))
- *ey = y_max;
- else if (inrange(y_min, iy, oy))
- *ey = y_min;
- else {
- graph_error("error in edge_intersect");
- }
- return;
- }
- /* slanted line of some kind */
-
- /* does it intersect y_min edge */
- if (inrange(y_min, iy, oy) && y_min != iy && y_min != oy) {
- x = ix + (y_min - iy) * ((ox - ix) / (oy - iy));
- if (inrange(x, x_min, x_max)) {
- *ex = x;
- *ey = y_min;
- return; /* yes */
- }
- }
- /* does it intersect y_max edge */
- if (inrange(y_max, iy, oy) && y_max != iy && y_max != oy) {
- x = ix + (y_max - iy) * ((ox - ix) / (oy - iy));
- if (inrange(x, x_min, x_max)) {
- *ex = x;
- *ey = y_max;
- return; /* yes */
- }
- }
- /* does it intersect x_min edge */
- if (inrange(x_min, ix, ox) && x_min != ix && x_min != ox) {
- y = iy + (x_min - ix) * ((oy - iy) / (ox - ix));
- if (inrange(y, y_min, y_max)) {
- *ex = x_min;
- *ey = y;
- return;
- }
- }
- /* does it intersect x_max edge */
- if (inrange(x_max, ix, ox) && x_max != ix && x_max != ox) {
- y = iy + (x_max - ix) * ((oy - iy) / (ox - ix));
- if (inrange(y, y_min, y_max)) {
- *ex = x_max;
- *ey = y;
- return;
- }
- }
- /* If we reach here, the inrange point is on the edge, and
- * the line segment from the outrange point does not cross any
- * other edges to get there. In this case, we return the inrange
- * point as the 'edge' intersection point. This will basically draw
- * line.
- */
- *ex = ix;
- *ey = iy;
- return;
- }
-
- /* XXX - JG */
- /* single edge intersection algorithm for "steps" curves */
- /*
- * Given two points, one inside and one outside the plot, return
- * the point where an edge of the plot intersects the line segments
- * forming the step between the two points.
- *
- * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
- * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
- * (x2,y1)->(x2,y2).
- */
- static void edge_intersect_steps(points, i, ex, ey)
- struct coordinate GPHUGE *points; /* the points array */
- int i; /* line segment from point i-1 to point i */
- double *ex, *ey; /* the point where it crosses an edge */
- {
- /* global x_min, x_max, y_min, x_max */
- double ax = points[i - 1].x;
- double ay = points[i - 1].y;
- double bx = points[i].x;
- double by = points[i].y;
-
- if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
- if (inrange(ay, y_min, y_max)) {
- *ey = ay;
- if (ax > x_max)
- *ex = x_max;
- else /* x < x_min */
- *ex = x_min;
- } else {
- *ex = bx;
- if (ay > y_max)
- *ey = y_max;
- else /* y < y_min */
- *ey = y_min;
- }
- } else { /* from INRANGE to OUTRANGE */
- if (inrange(bx, x_min, x_max)) {
- *ex = bx;
- if (by > y_max)
- *ey = y_max;
- else /* y < y_min */
- *ey = y_min;
- } else {
- *ey = ay;
- if (bx > x_max)
- *ex = x_max;
- else /* x < x_min */
- *ex = x_min;
- }
- }
- return;
- }
-
- /* XXX - HOE */
- /* single edge intersection algorithm for "fsteps" curves */
- /* fsteps means step on forward y-value.
- * Given two points, one inside and one outside the plot, return
- * the point where an edge of the plot intersects the line segments
- * forming the step between the two points.
- *
- * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
- * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
- * (x1,y2)->(x2,y2).
- */
- static void edge_intersect_fsteps(points, i, ex, ey)
- struct coordinate GPHUGE *points; /* the points array */
- int i; /* line segment from point i-1 to point i */
- double *ex, *ey; /* the point where it crosses an edge */
- {
- /* global x_min, x_max, y_min, x_max */
- double ax = points[i - 1].x;
- double ay = points[i - 1].y;
- double bx = points[i].x;
- double by = points[i].y;
-
- if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
- if (inrange(ax, x_min, x_max)) {
- *ex = ax;
- if (ay > y_max)
- *ey = y_max;
- else /* y < y_min */
- *ey = y_min;
- } else {
- *ey = by;
- if (bx > x_max)
- *ex = x_max;
- else /* x < x_min */
- *ex = x_min;
- }
- } else { /* from INRANGE to OUTRANGE */
- if (inrange(by, y_min, y_max)) {
- *ey = by;
- if (bx > x_max)
- *ex = x_max;
- else /* x < x_min */
- *ex = x_min;
- } else {
- *ex = ax;
- if (by > y_max)
- *ey = y_max;
- else /* y < y_min */
- *ey = y_min;
- }
- }
- return;
- }
-
- /* XXX - JG */
- /* double edge intersection algorithm for "steps" plot */
- /* Given two points, both outside the plot, return the points where an
- * edge of the plot intersects the line segments forming a step
- * by the two points. There may be zero, one, two, or an infinite number
- * of intersection points. (One means an intersection at a corner, infinite
- * means overlaying the edge itself). We return FALSE when there is nothing
- * to draw (zero intersections), and TRUE when there is something to
- * draw (the one-point case is a degenerate of the two-point case and we do
- * not distinguish it - we draw it anyway).
- *
- * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
- * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
- * (x2,y1)->(x2,y2).
- */
- static TBOOLEAN /* any intersection? */
- two_edge_intersect_steps(points, i, lx, ly)
- struct coordinate GPHUGE *points; /* the points array */
- int i; /* line segment from point i-1 to point i */
- double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
- {
- /* global x_min, x_max, y_min, x_max */
- double ax = points[i - 1].x;
- double ay = points[i - 1].y;
- double bx = points[i].x;
- double by = points[i].y;
-
- if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
- GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
- ((ay > y_max || ay < y_min) &&
- (bx > x_max || bx < x_min))) {
- return (FALSE);
- } else if (inrange(ay, y_min, y_max) && inrange(bx, x_min, x_max)) { /* corner of step inside plotspace */
- *ly++ = ay;
- if (ax < x_min)
- *lx++ = x_min;
- else
- *lx++ = x_max;
-
- *lx++ = bx;
- if (by < y_min)
- *ly++ = y_min;
- else
- *ly++ = y_max;
-
- return (TRUE);
- } else if (inrange(ay, y_min, y_max)) { /* cross plotspace in x-direction */
- *lx++ = x_min;
- *ly++ = ay;
- *lx++ = x_max;
- *ly++ = ay;
- return (TRUE);
- } else if (inrange(ax, x_min, x_max)) { /* cross plotspace in y-direction */
- *lx++ = bx;
- *ly++ = y_min;
- *lx++ = bx;
- *ly++ = y_max;
- return (TRUE);
- } else
- return (FALSE);
- }
-
- /* XXX - HOE */
- /* double edge intersection algorithm for "fsteps" plot */
- /* Given two points, both outside the plot, return~L e an
- * edge of the plot intersects the line segments forming a step
- * by the two points. There may be zero, one, two, or an infinite number
- * of intersection points. (One means an intersection at a corner, infinite
- * means overlaying the edge itself). We return FALSE when there is nothing
- * to draw (zero intersections), and TRUE when there is something to
- * draw (the one-point case is a degenerate of the two-point case and we do
- * not distinguish it - we draw it anyway).
- *
- * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
- * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
- * (x1,y2)->(x2,y2).
- */
- static TBOOLEAN /* any intersection? */
- two_edge_intersect_fsteps(points, i, lx, ly)
- struct coordinate GPHUGE *points; /* the points array */
- int i; /* line segment from point i-1 to point i */
- double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
- {
- /* global x_min, x_max, y_min, x_max */
- double ax = points[i - 1].x;
- double ay = points[i - 1].y;
- double bx = points[i].x;
- double by = points[i].y;
-
- if (GPMAX(ax, bx) < x_min || GPMIN(ax, bx) > x_max ||
- GPMAX(ay, by) < y_min || GPMIN(ay, by) > y_max ||
- ((by > y_max || by < y_min) &&
- (ax > x_max || ax < x_min))) {
- return (FALSE);
- } else if (inrange(by, y_min, y_max) && inrange(ax, x_min, x_max)) { /* corner of step inside plotspace */
- *lx++ = ax;
- if (ay < y_min)
- *ly++ = y_min;
- else
- *ly++ = y_max;
-
- *ly = by;
- if (bx < x_min)
- *lx = x_min;
- else
- *lx = x_max;
-
- return (TRUE);
- } else if (inrange(by, y_min, y_max)) { /* cross plotspace in x-direction */
- *lx++ = x_min;
- *ly++ = by;
- *lx = x_max;
- *ly = by;
- return (TRUE);
- } else if (inrange(ax, x_min, x_max)) { /* cross plotspace in y-direction */
- *lx++ = ax;
- *ly++ = y_min;
- *lx = ax;
- *ly = y_max;
- return (TRUE);
- } else
- return (FALSE);
- }
-
- /* double edge intersection algorithm */
- /* Given two points, both outside the plot, return
- * the points where an edge of the plot intersects the line segment defined
- * by the two points. There may be zero, one, two, or an infinite number
- * of intersection points. (One means an intersection at a corner, infinite
- * means overlaying the edge itself). We return FALSE when there is nothing
- * to draw (zero intersections), and TRUE when there is something to
- * draw (the one-point case is a degenerate of the two-point case and we do
- * not distinguish it - we draw it anyway).
- */
- static TBOOLEAN /* any intersection? */
- two_edge_intersect(points, i, lx, ly)
- struct coordinate GPHUGE *points; /* the points array */
- int i; /* line segment from point i-1 to point i */
- double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
- {
- /* global x_min, x_max, y_min, x_max */
- int count;
- double ix = points[i - 1].x;
- double iy = points[i - 1].y;
- double ox = points[i].x;
- double oy = points[i].y;
- double t[4];
- double swap;
- double t_min, t_max;
- #if 0
- fprintf(stderr, "\ntwo_edge_intersect (%g, %g) and (%g, %g) : ",
- points[i - 1].x, points[i - 1].y,
- points[i].x, points[i].y);
- #endif
- /* nasty degenerate cases, effectively drawing to an infinity point (?)
- cope with them here, so don't process them as a "real" OUTRANGE point
-
- If more than one coord is -VERYLARGE, then can't ratio the "infinities"
- so drop out by returning FALSE */
-
- count = 0;
- if (ix == -VERYLARGE)
- count++;
- if (ox == -VERYLARGE)
- count++;
- if (iy == -VERYLARGE)
- count++;
- if (oy == -VERYLARGE)
- count++;
-
- /* either doesn't pass through graph area *or*
- can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
- if (count > 1) {
- #if 0
- fprintf(stderr, "\tA\n");
- #endif
- return (FALSE);
- }
- if (ox == -VERYLARGE || ix == -VERYLARGE) {
- if (ix == -VERYLARGE) {
- /* swap points so ix/iy don't have a -VERYLARGE component */
- swap = ix;
- ix = ox;
- ox = swap;
- swap = iy;
- iy = oy;
- oy = swap;
- }
- /* check actually passes through the graph area */
- if (ix > x_max && inrange(iy, y_min, y_max)) {
- lx[0] = x_min;
- ly[0] = iy;
-
- lx[1] = x_max;
- ly[1] = iy;
- #if 0
- fprintf(stderr, "(%g %g) -> (%g %g)",
- lx[0], ly[0], lx[1], ly[1]);
- #endif
- return (TRUE);
- } else {
- #if 0
- fprintf(stderr, "\tB\n");
- #endif
- return (FALSE);
- }
- }
- if (oy == -VERYLARGE || iy == -VERYLARGE) {
- if (iy == -VERYLARGE) {
- /* swap points so ix/iy don't have a -VERYLARGE component */
- swap = ix;
- ix = ox;
- ox = swap;
- swap = iy;
- iy = oy;
- oy = swap;
- }
- /* check actually passes through the graph area */
- if (iy > y_max && inrange(ix, x_min, x_max)) {
- lx[0] = ix;
- ly[0] = y_min;
-
- lx[1] = ix;
- ly[1] = y_max;
- #if 0
- fprintf(stderr, "(%g %g) -> (%g %g)",
- lx[0], ly[0], lx[1], ly[1]);
- #endif
- return (TRUE);
- } else {
- #if 0
- fprintf(stderr, "\tC\n");
- #endif
- return (FALSE);
- }
- }
- /*
- * Special horizontal/vertical, etc. cases are checked and remaining
- * slant lines are checked separately.
- *
- * The slant line intersections are solved using the parametric form
- * of the equation for a line, since if we test x/y min/max planes explicitly
- * then e.g. a line passing through a corner point (x_min,y_min)
- * actually intersects 2 planes and hence further tests would be required
- * to anticipate this and similar situations.
- */
-
- /*
- * Can have case (ix == ox && iy == oy) as both points OUTRANGE
- */
- if (ix == ox && iy == oy) {
- /* but as only define single outrange point, can't intersect graph area */
- return (FALSE);
- }
- if (ix == ox) {
- /* line parallel to y axis */
-
- /* x coord must be in range, and line must span both y_min and y_max */
- /* note that spanning y_min implies spanning y_max, as both points OUTRANGE */
- if (!inrange(ix, x_min, x_max)) {
- return (FALSE);
- }
- if (inrange(y_min, iy, oy)) {
- lx[0] = ix;
- ly[0] = y_min;
-
- lx[1] = ix;
- ly[1] = y_max;
- #if 0
- fprintf(stderr, "(%g %g) -> (%g %g)",
- lx[0], ly[0], lx[1], ly[1]);
- #endif
- return (TRUE);
- } else
- return (FALSE);
- }
- if (iy == oy) {
- /* already checked case (ix == ox && iy == oy) */
-
- /* line parallel to x axis */
- /* y coord must be in range, and line must span both x_min and x_max */
- /* note that spanning x_min implies spanning x_max, as both points OUTRANGE */
- if (!inrange(iy, y_min, y_max)) {
- return (FALSE);
- }
- if (inrange(x_min, ix, ox)) {
- lx[0] = x_min;
- ly[0] = iy;
-
- lx[1] = x_max;
- ly[1] = iy;
- #if 0
- fprintf(stderr, "(%g %g) -> (%g %g)",
- lx[0], ly[0], lx[1], ly[1]);
- #endif
- return (TRUE);
- } else
- return (FALSE);
- }
- /* nasty 2D slanted line in an xy plane */
-
- /*
- Solve parametric equation
-
- (ix, iy) + t (diff_x, diff_y)
-
- where 0.0 <= t <= 1.0 and
-
- diff_x = (ox - ix);
- diff_y = (oy - iy);
- */
-
- t[0] = (x_min - ix) / (ox - ix);
- t[1] = (x_max - ix) / (ox - ix);
-
- if (t[0] > t[1]) {
- swap = t[0];
- t[0] = t[1];
- t[1] = swap;
- }
- t[2] = (y_min - iy) / (oy - iy);
- t[3] = (y_max - iy) / (oy - iy);
-
- if (t[2] > t[3]) {
- swap = t[2];
- t[2] = t[3];
- t[3] = swap;
- }
- t_min = GPMAX(GPMAX(t[0], t[2]), 0.0);
- t_max = GPMIN(GPMIN(t[1], t[3]), 1.0);
-
- if (t_min > t_max)
- return (FALSE);
-
- lx[0] = ix + t_min * (ox - ix);
- ly[0] = iy + t_min * (oy - iy);
-
- lx[1] = ix + t_max * (ox - ix);
- ly[1] = iy + t_max * (oy - iy);
-
- /*
- * Can only have 0 or 2 intersection points -- only need test one coord
- */
- if (inrange(lx[0], x_min, x_max) &&
- inrange(ly[0], y_min, y_max)) {
- #if 0
- fprintf(stderr, "(%g %g) -> (%g %g)",
- lx[0], ly[0], lx[1], ly[1]);
- #endif
- return (TRUE);
- }
- return (FALSE);
- }
-
- /* justify ticplace to a proper date-time value */
- double time_tic_just(level, ticplace)
- int level;
- double ticplace;
- {
- struct tm tm;
-
- if (level <= 0) {
- return (ticplace);
- }
- ggmtime(&tm, (double) ticplace);
- if (level > 0) { /* units of minutes */
- if (tm.tm_sec > 50)
- tm.tm_min++;
- tm.tm_sec = 0;
- }
- if (level > 1) { /* units of hours */
- if (tm.tm_min > 50)
- tm.tm_hour++;
- tm.tm_min = 0;
- }
- if (level > 2) { /* units of days */
- if (tm.tm_hour > 14) {
- tm.tm_hour = 0;
- tm.tm_mday = 0;
- tm.tm_yday++;
- ggmtime(&tm, (double) gtimegm(&tm));
- } else if (tm.tm_hour > 7) {
- tm.tm_hour = 12;
- } else if (tm.tm_hour > 3) {
- tm.tm_hour = 6;
- } else {
- tm.tm_hour = 0;
- }
- }
- /* skip it, I have not bothered with weekday so far */
- if (level > 4) { /* units of month */
- if (tm.tm_mday > 25) {
- tm.tm_mon++;
- if (tm.tm_mon > 11) {
- tm.tm_year++;
- tm.tm_mon = 0;
- }
- }
- tm.tm_mday = 1;
- }
- if (level > 5) {
- if (tm.tm_mon >= 7)
- tm.tm_year++;
- tm.tm_mon = 0;
- }
- ticplace = (double) gtimegm(&tm);
- ggmtime(&tm, (double) gtimegm(&tm));
- return (ticplace);
- }
-
- /* make smalltics for time-axis */
- double make_ltic(tlevel, incr)
- int tlevel;
- double incr;
- {
- double tinc;
- tinc = 0;
- if (tlevel < 0)
- tlevel = 0;
- switch (tlevel) {
- case 0:
- case 1:{
- if (incr >= 20)
- tinc = 10;
- if (incr >= 60)
- tinc = 30;
- if (incr >= 2 * 60)
- tinc = 60;
- if (incr >= 5 * 60)
- tinc = 2 * 60;
- if (incr >= 10 * 60)
- tinc = 5 * 60;
- if (incr >= 20 * 60)
- tinc = 10 * 60;
- break;
- }
- case 2:{
- if (incr >= 20 * 60)
- tinc = 10 * 60;
- if (incr >= 3600)
- tinc = 30 * 60;
- if (incr >= 2 * 3600)
- tinc = 3600;
- if (incr >= 5 * 3600)
- tinc = 2 * 3600;
- if (incr >= 10 * 3600)
- tinc = 5 * 3600;
- if (incr >= 20 * 3600)
- tinc = 10 * 3600;
- break;
- }
- case 3:{
- if (incr > 2 * 3600)
- tinc = 3600;
- if (incr > 4 * 3600)
- tinc = 2 * 3600;
- if (incr > 7 * 3600)
- tinc = 3 * 3600;
- if (incr > 13 * 3600)
- tinc = 6 * 3600;
- if (incr > DAY_SEC)
- tinc = 12 * 3600;
- if (incr > 2 * DAY_SEC)
- tinc = DAY_SEC;
- break;
- }
- case 4:{ /* week: tic per day */
- if (incr > 2 * DAY_SEC)
- tinc = DAY_SEC;
- if (incr > 7 * DAY_SEC)
- tinc = 7 * DAY_SEC;
- break;
- }
- case 5:{ /* month */
- if (incr > 2 * DAY_SEC)
- tinc = DAY_SEC;
- if (incr > 15 * DAY_SEC)
- tinc = 10 * DAY_SEC;
- if (incr > 2 * MON_SEC)
- tinc = MON_SEC;
- if (incr > 6 * MON_SEC)
- tinc = 3 * MON_SEC;
- if (incr > 2 * YEAR_SEC)
- tinc = YEAR_SEC;
- break;
- }
- case 6:{ /* year */
- if (incr > 2 * MON_SEC)
- tinc = MON_SEC;
- if (incr > 6 * MON_SEC)
- tinc = 3 * MON_SEC;
- if (incr > 2 * YEAR_SEC)
- tinc = YEAR_SEC;
- if (incr > 10 * YEAR_SEC)
- tinc = 5 * YEAR_SEC;
- if (incr > 50 * YEAR_SEC)
- tinc = 10 * YEAR_SEC;
- if (incr > 100 * YEAR_SEC)
- tinc = 20 * YEAR_SEC;
- if (incr > 200 * YEAR_SEC)
- tinc = 50 * YEAR_SEC;
- if (incr > 300 * YEAR_SEC)
- tinc = 100 * YEAR_SEC;
- break;
- }
- }
- return (tinc);
- }
-
-
- void write_multiline(x, y, text, hor, vert, angle, font)
- unsigned int x, y;
- char *text;
- enum JUSTIFY hor; /* horizontal ... */
- int vert; /* ... and vertical just - text in hor direction despite angle */
- int angle; /* assume term has already been set for this */
- char *font; /* NULL or "" means use default */
- {
- register struct termentry *t = term;
- char *p = text;
-
- if (!p)
- return;
-
- if (vert != JUST_TOP) {
- /* count lines and adjust y */
- int lines = 0; /* number of linefeeds - one fewer than lines */
- while (*p++) {
- if (*p == '\n')
- ++lines;
- }
- if (angle)
- x -= (vert * lines * t->v_char) / 2;
- else
- y += (vert * lines * t->v_char) / 2;
- }
- if (font && *font)
- (*t->set_font) (font);
-
-
- for (;;) { /* we will explicitly break out */
-
- if ((text != NULL) && (p = strchr(text, '\n')) != NULL)
- *p = 0; /* terminate the string */
-
- if ((*t->justify_text) (hor)) {
- (*t->put_text) (x, y, text);
- } else {
- int fix = hor * (t->h_char) * strlen(text) / 2;
- if (angle)
- (*t->put_text) (x, y - fix, text);
- else
- (*t->put_text) (x - fix, y, text);
- }
- if (angle)
- x += t->v_char;
- else
- y -= t->v_char;
-
- if (!p)
- break;
- else {
- /* put it back */
- *p = '\n';
- }
-
- text = p + 1;
- } /* unconditional branch back to the for(;;) - just a goto ! */
-
- if (font && *font)
- (*t->set_font) (default_font);
-
- }
-
- /* display a x-axis ticmark - called by gen_ticks */
- /* also uses global tic_start, tic_direction, tic_text and tic_just */
- static void xtick2d_callback(axis, place, text, grid)
- int axis;
- double place;
- char *text;
- struct lp_style_type grid; /* linetype or -2 for no grid */
- {
- register struct termentry *t = term;
- /* minitick if text is NULL - beware - h_tic is unsigned */
- int ticsize = tic_direction * (int) (t->v_tic) * (text ? ticscale : miniticscale);
- unsigned int x = map_x(place);
-
- if (grid.l_type > -2) {
- term_apply_lp_properties(&grid);
- if (polar_grid_angle) {
- double x = place, y = 0, s = sin(0.1), c = cos(0.1);
- int i;
- int ogx = map_x(x);
- int ogy = map_y(0);
- int tmpgx, tmpgy, gx, gy;
-
- if (place > largest_polar_circle)
- largest_polar_circle = place;
- else if (-place > largest_polar_circle)
- largest_polar_circle = -place;
- for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
- {
- /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
- double tx = x * c - y * s;
- /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
- y = y * c + x * s;
- x = tx;
- }
- tmpgx = gx = map_x(x);
- tmpgy = gy = map_y(y);
- if (clip_line(&ogx, &ogy, &tmpgx, &tmpgy)) {
- (*t->move) ((unsigned int) ogx, (unsigned int) ogy);
- (*t->vector) ((unsigned int) tmpgx, (unsigned int) tmpgy);
- }
- ogx = gx;
- ogy = gy;
- }
- } else {
- if (lkey && key_yt > ybot && x < key_xr && x > key_xl) {
- if (key_yb > ybot) {
- (*t->move) (x, ybot);
- (*t->vector) (x, key_yb);
- }
- if (key_yt < ytop) {
- (*t->move) (x, key_yt);
- (*t->vector) (x, ytop);
- }
- } else {
- (*t->move) (x, ybot);
- (*t->vector) (x, ytop);
- }
- }
- term_apply_lp_properties(&border_lp); /* border linetype */
- }
- /* we precomputed tic posn and text posn in global vars */
-
- (*t->move) (x, tic_start);
- (*t->vector) (x, tic_start + ticsize);
-
- if (tic_mirror >= 0) {
- (*t->move) (x, tic_mirror);
- (*t->vector) (x, tic_mirror - ticsize);
- }
- if (text)
- write_multiline(x, tic_text, text, tic_hjust, tic_vjust, rotate_tics, NULL);
- }
-
- /* display a y-axis ticmark - called by gen_ticks */
- /* also uses global tic_start, tic_direction, tic_text and tic_just */
- static void ytick2d_callback(axis, place, text, grid)
- int axis;
- double place;
- char *text;
- struct lp_style_type grid; /* linetype or -2 */
- {
- register struct termentry *t = term;
- /* minitick if text is NULL - v_tic is unsigned */
- int ticsize = tic_direction * (int) (t->h_tic) * (text ? ticscale : miniticscale);
- unsigned int y = map_y(place);
- if (grid.l_type > -2) {
- term_apply_lp_properties(&grid);
- if (polar_grid_angle) {
- double x = 0, y = place, s = sin(0.1), c = cos(0.1);
- int i;
- if (place > largest_polar_circle)
- largest_polar_circle = place;
- else if (-place > largest_polar_circle)
- largest_polar_circle = -place;
- clip_move(map_x(x), map_y(y));
- for (i = 1; i <= 63 /* 2pi/0.1 */ ; ++i) {
- {
- /* cos(t+dt) = cos(t)cos(dt)-sin(t)cos(dt) */
- double tx = x * c - y * s;
- /* sin(t+dt) = sin(t)cos(dt)+cos(t)sin(dt) */
- y = y * c + x * s;
- x = tx;
- }
- clip_vector(map_x(x), map_y(y));
- }
- } else {
- if (lkey && y < key_yt && y > key_yb && key_xl < xright /* catch TOUT */ ) {
- if (key_xl > xleft) {
- (*t->move) (xleft, y);
- (*t->vector) (key_xl, y);
- }
- if (key_xr < xright) {
- (*t->move) (key_xr, y);
- (*t->vector) (xright, y);
- }
- } else {
- (*t->move) (xleft, y);
- (*t->vector) (xright, y);
- }
- }
- term_apply_lp_properties(&border_lp); /* border linetype */
- }
- /* we precomputed tic posn and text posn */
-
- (*t->move) (tic_start, y);
- (*t->vector) (tic_start + ticsize, y);
-
- if (tic_mirror >= 0) {
- (*t->move) (tic_mirror, y);
- (*t->vector) (tic_mirror - ticsize, y);
- }
- if (text)
- write_multiline(tic_text, y, text, tic_hjust, tic_vjust, rotate_tics, NULL);
- }
-
- int label_width(str, lines)
- char *str;
- int *lines;
- {
- char lab[MAX_LINE_LEN + 1], *s, *e;
- int mlen, len, l;
-
- l = mlen = len = 0;
- sprintf(lab, "%s\n", str);
- s = lab;
- while ((e = (char *) strchr(s, '\n')) != NULL) { /* HBB 980308: quiet BC-3.1 warning */
- *e = '\0';
- len = strlen(s); /* = e-s ? */
- if (len > mlen)
- mlen = len;
- if (len || l)
- l++;
- s = ++e;
- }
- /* lines = NULL => not interested - div */
- if (lines)
- *lines = l;
- return (mlen);
- }
-
-
- void setup_tics(axis, ticdef, format, max)
- int axis;
- struct ticdef *ticdef;
- char *format;
- int max; /* approx max number of slots available */
- {
- double tic = 0; /* HBB: shut up gcc -Wall */
-
- int fixmin = (auto_array[axis] & 1) != 0;
- int fixmax = (auto_array[axis] & 2) != 0;
-
- if (ticdef->type == TIC_SERIES) {
- ticstep[axis] = tic = ticdef->def.series.incr;
- fixmin &= (ticdef->def.series.start == -VERYLARGE);
- fixmax &= (ticdef->def.series.end == VERYLARGE);
- } else if (ticdef->type == TIC_COMPUTED) {
- ticstep[axis] = tic = make_tics(axis, max);
- } else {
- fixmin = fixmax = 0; /* user-defined, day or month */
- }
-
- if (fixmin) {
- if (min_array[axis] < max_array[axis])
- min_array[axis] = tic * floor(min_array[axis] / tic);
- else
- min_array[axis] = tic * ceil(min_array[axis] / tic);
- }
- if (fixmax) {
- if (min_array[axis] < max_array[axis])
- max_array[axis] = tic * ceil(max_array[axis] / tic);
- else
- max_array[axis] = tic * floor(max_array[axis] / tic);
- }
- if (datatype[axis] == TIME && format_is_numeric[axis])
- /* invent one for them */
- timetic_format(axis, min_array[axis], max_array[axis]);
- else
- strcpy(ticfmt[axis], format);
- }
-
- /*{{{ mant_exp - split into mantissa and/or exponent */
- static void mant_exp(log_base, x, scientific, m, p)
- double log_base, x;
- int scientific; /* round to power of 3 */
- double *m;
- int *p; /* results */
- {
- int sign = 1;
- double l10;
- int power;
- /*{{{ check 0 */
- if (x == 0) {
- if (m)
- *m = 0;
- if (p)
- *p = 0;
- return;
- }
- /*}}} */
- /*{{{ check -ve */
- if (x < 0) {
- sign = (-1);
- x = (-x);
- }
- /*}}} */
-
- l10 = log10(x) / log_base;
- power = floor(l10);
- if (scientific) {
- power = 3 * floor(power / 3.0);
- }
- if (m)
- *m = sign * pow(10.0, (l10 - power) * log_base);
- if (p)
- *p = power;
- }
- /*}}} */
-
- /*
- * Kludge alert!!
- * Workaround until we have a better solution ...
- * Note: this assumes that all calls to sprintf in gprintf have
- * exactly three args. Lars
- */
- #ifdef HAVE_SNPRINTF
- # define sprintf(str,fmt,arg) \
- if (snprintf((str),count,(fmt),(arg)) > count) \
- fprintf (stderr,"%s:%d: Warning: too many digits for format\n",__FILE__,__LINE__)
- #endif
-
- /*{{{ gprintf */
- /* extended snprintf */
- static void gprintf(dest, count, format, log_base, x)
- char *dest, *format;
- size_t count;
- double log_base, x; /* we print one number in a number of different formats */
- {
- #define LOC_PI 3.14159265358979323846 /* local definition of PI */
- char temp[MAX_LINE_LEN];
- char *t;
-
- for (;;) {
- /*{{{ copy to dest until % */
- while (*format != '%')
- if (!(*dest++ = *format++))
- return; /* end of format */
- /*}}} */
-
- /*{{{ check for %% */
- if (format[1] == '%') {
- *dest++ = '%';
- format += 2;
- continue;
- }
- /*}}} */
-
- /*{{{ copy format part to temp, excluding conversion character */
- t = temp;
- *t++ = '%';
- /* dont put isdigit first since sideeffect in macro is bad */
- while (*++format == '.' || isdigit((int) *format) ||
- *format == '-' || *format == '+' || *format == ' ')
- *t++ = *format;
- /*}}} */
-
- /*{{{ convert conversion character */
- switch (*format) {
- /*{{{ x and o */
- case 'x':
- case 'X':
- case 'o':
- case 'O':
- t[0] = *format++;
- t[1] = 0;
- sprintf(dest, temp, (int) x);
- dest += strlen(dest);
- break;
- /*}}} */
- /*{{{ e, f and g */
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'g':
- case 'G':
- t[0] = *format++;
- t[1] = 0;
- sprintf(dest, temp, x);
- dest += strlen(dest);
- break;
- /*}}} */
- /*{{{ l */
- case 'l':
- {
- double mantissa;
- mant_exp(log_base, x, 0, &mantissa, NULL);
- t[0] = 'f';
- t[1] = 0;
- sprintf(dest, temp, mantissa);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- /*{{{ t */
- case 't':
- {
- double mantissa;
- mant_exp(1.0, x, 0, &mantissa, NULL);
- t[0] = 'f';
- t[1] = 0;
- sprintf(dest, temp, mantissa);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- /*{{{ s */
- case 's':
- {
- double mantissa;
- mant_exp(1.0, x, 1, &mantissa, NULL);
- t[0] = 'f';
- t[1] = 0;
- sprintf(dest, temp, mantissa);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- /*{{{ L */
- case 'L':
- {
- int power;
- mant_exp(log_base, x, 0, NULL, &power);
- t[0] = 'd';
- t[1] = 0;
- sprintf(dest, temp, power);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- /*{{{ T */
- case 'T':
- {
- int power;
- mant_exp(1.0, x, 0, NULL, &power);
- t[0] = 'd';
- t[1] = 0;
- sprintf(dest, temp, power);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- /*{{{ S */
- case 'S':
- {
- int power;
- mant_exp(1.0, x, 1, NULL, &power);
- t[0] = 'd';
- t[1] = 0;
- sprintf(dest, temp, power);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- /*{{{ c */
- case 'c':
- {
- int power;
- mant_exp(1.0, x, 1, NULL, &power);
- t[0] = 'c';
- t[1] = 0;
- power = power / 3 + 6; /* -18 -> 0, 0 -> 6, +18 -> 12, ... */
- if (power >= 0 && power <= 12) {
- sprintf(dest, temp, "afpnum kMGTPE"[power]);
- } else {
- /* please extend the range ! */
- /* name power name power
- -------------------------
- atto -18 Exa 18
- femto -15 Peta 15
- pico -12 Tera 12
- nano -9 Giga 9
- micro -6 Mega 6
- milli -3 kilo 3 */
-
- /* for the moment, print e+21 for example */
- sprintf(dest, "e%+02d", (power - 6) * 3);
- }
-
- dest += strlen(dest);
- ++format;
- break;
- }
- case 'P':
- {
- t[0] = 'f';
- t[1] = 0;
- sprintf(dest, temp, x / LOC_PI);
- dest += strlen(dest);
- ++format;
- break;
- }
- /*}}} */
- default:
- int_error("Bad format character", NO_CARET);
- }
- /*}}} */
- }
- }
- #undef LOC_PI /* local definition of PI */
- /*}}} */
- #ifdef HAVE_SNPRINTF
- # undef sprintf
- #endif
-
-
-
- /*{{{ gen_tics */
- /* uses global arrays ticstep[], ticfmt[], min_array[], max_array[],
- * auto_array[], log_array[], log_base_array[]
- * we use any of GRID_X/Y/X2/Y2 and _MX/_MX2/etc - caller is expected
- * to clear the irrelevent fields from global grid bitmask
- * note this is also called from graph3d, so we need GRID_Z too
- */
-
- void gen_tics(axis, def, grid, minitics, minifreq, callback)
- int axis; /* FIRST_X_AXIS, etc */
- struct ticdef *def; /* tic defn */
- int grid; /* GRID_X | GRID_MX etc */
- int minitics; /* minitics - off/default/auto/explicit */
- double minifreq; /* frequency */
- tic_callback callback; /* fn to call to actually do the work */
- {
- /* seperate main-tic part of grid */
- struct lp_style_type lgrd, mgrd;
-
- memcpy(&lgrd, &grid_lp, sizeof(struct lp_style_type));
- memcpy(&mgrd, &mgrid_lp, sizeof(struct lp_style_type));
- lgrd.l_type = (grid & (GRID_X | GRID_Y | GRID_X2 | GRID_Y2 | GRID_Z)) ? grid_lp.l_type : -2;
- mgrd.l_type = (grid & (GRID_MX | GRID_MY | GRID_MX2 | GRID_MY2 | GRID_MZ)) ? mgrid_lp.l_type : -2;
-
- if (def->type == TIC_USER) { /* special case */
- /*{{{ do user tics then return */
- struct ticmark *mark = def->def.user;
- double uncertain = (max_array[axis] - min_array[axis]) / 10;
- double ticmin = min_array[axis] - SIGNIF * uncertain;
- double internal_max = max_array[axis] + SIGNIF * uncertain;
- double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
-
- /* polar labels always +ve, and if rmin has been set, they are
- * relative to rmin. position is as user specified, but must
- * be translated. I dont think it will work at all for
- * log scale, so I shan't worry about it !
- */
- double polar_shift = (polar && !(autoscale_r & 1)) ? rmin : 0;
-
- for (mark = def->def.user; mark; mark = mark->next) {
- char label[64];
- double internal = log_array[axis] ? log(mark->position) / log_base_array[axis] : mark->position;
-
- internal -= polar_shift;
-
- if (!inrange(internal, ticmin, internal_max))
- continue;
-
- if (datatype[axis] == TIME)
- gstrftime(label, 24, mark->label ? mark->label : ticfmt[axis], mark->position);
- else
- gprintf(label, sizeof(label), mark->label ? mark->label : ticfmt[axis], log_base, mark->position);
-
- (*callback) (axis, internal, label, lgrd);
- }
-
- return; /* NO MINITICS FOR USER-DEF TICS */
- /*}}} */
- }
- /* series-tics
- * need to distinguish user co-ords from internal co-ords.
- * - for logscale, internal = log(user), else internal = user
- *
- * The minitics are a bit of a drag - we need to distinuish
- * the cases step>1 from step == 1.
- * If step = 1, we are looking at 1,10,100,1000 for example, so
- * minitics are 2,5,8, ... - done in user co-ordinates
- * If step>1, we are looking at 1,1e6,1e12 for example, so
- * minitics are 10,100,1000,... - done in internal co-ords
- */
-
- {
- double tic; /* loop counter */
- double internal; /* in internal co-ords */
- double user; /* in user co-ords */
- double start, step, end;
- double lmin = min_array[axis], lmax = max_array[axis];
- double internal_min, internal_max; /* to allow for rounding errors */
- double ministart = 0, ministep = 1, miniend = 1; /* internal or user - depends on step */
- int anyticput = 0; /* for detection of infinite loop */
-
- /* gprintf uses log10() of base - log_base_array is log() */
- double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
-
- if (lmax < lmin) {
- /* hmm - they have set reversed range for some reason */
- double temp = lmin;
- lmin = lmax;
- lmax = temp;
- }
- /*{{{ choose start, step and end */
- switch (def->type) {
- case TIC_SERIES:
- if (log_array[axis]) {
- /* we can tolerate start <= 0 if step and end > 0 */
- if (def->def.series.end <= 0 ||
- def->def.series.incr <= 0)
- return; /* just quietly ignore */
- step = log(def->def.series.incr) / log_base_array[axis];
- end = log(def->def.series.end) / log_base_array[axis];
- start = def->def.series.start > 0 ?
- log(def->def.series.start) / log_base_array[axis] :
- step;
- } else {
- start = def->def.series.start;
- step = def->def.series.incr;
- end = def->def.series.end;
- if (start == -VERYLARGE)
- start = step * floor(lmin / step);
- if (end == VERYLARGE)
- end = step * ceil(lmax / step);
- }
- break;
- case TIC_COMPUTED:
- /* round to multiple of step */
- start = ticstep[axis] * floor(lmin / ticstep[axis]);
- step = ticstep[axis];
- end = ticstep[axis] * ceil(lmax / ticstep[axis]);
- break;
- case TIC_MONTH:
- start = floor(lmin);
- end = ceil(lmax);
- step = floor((end - start) / 12);
- if (step < 1)
- step = 1;
- break;
- case TIC_DAY:
- start = floor(lmin);
- end = ceil(lmax);
- step = floor((end - start) / 14);
- if (step < 1)
- step = 1;
- break;
- default:
- graph_error("Internal error : unknown tic type");
- return; /* avoid gcc -Wall warning about start */
- }
- /*}}} */
-
- /*{{{ ensure ascending order */
- if (end < start) {
- double temp;
- temp = end;
- end = start;
- start = temp;
- }
- step = fabs(step);
- /*}}} */
-
- if (minitics) {
- /*{{{ figure out ministart, ministep, miniend */
- if (minitics == MINI_USER) {
- /* they have said what they want */
- if (minifreq <= 0)
- minitics = 0; /* not much else we can do */
- else if (log_array[axis]) {
- ministart = ministep = step / minifreq * base_array[axis];
- miniend = step * base_array[axis];
- } else {
- ministart = ministep = step / minifreq;
- miniend = step;
- }
- } else if (log_array[axis]) {
- if (step > 1.5) { /* beware rounding errors */
- /*{{{ 10,100,1000 case */
- /* no more than five minitics */
- ministart = ministep = (int) (0.2 * step);
- if (ministep < 1)
- ministart = ministep = 1;
- miniend = step;
- /*}}} */
- } else {
- /*{{{ 2,5,8 case */
- miniend = base_array[axis];
- if (end - start >= 10)
- minitics = 0; /* none */
- else if (end - start >= 5) {
- ministart = 2;
- ministep = 3;
- } else {
- ministart = 2;
- ministep = 1;
- }
- /*}}} */
- }
- } else if (datatype[axis] == TIME) {
- ministart = ministep = make_ltic(timelevel[axis], step);
- miniend = step * 0.9;
- } else if (minitics == MINI_AUTO) {
- ministart = ministep = 0.1 * step;
- miniend = step;
- } else
- minitics = 0;
-
- if (ministep <= 0)
- minitics = 0; /* dont get stuck in infinite loop */
- /*}}} */
- }
- /*{{{ a few tweaks and checks */
- /* watch rounding errors */
- end += SIGNIF * step;
- internal_max = lmax + step * SIGNIF;
- internal_min = lmin - step * SIGNIF;
-
- if (step == 0)
- return; /* just quietly ignore them ! */
- /*}}} */
-
- for (tic = start; tic <= end; tic += step) {
- if (anyticput == 2) /* See below... */
- break;
- if (anyticput && (fabs(tic - start) < DBL_EPSILON)) {
- /* step is too small.. */
- anyticput = 2; /* Don't try again. */
- tic = end; /* Put end tic. */
- } else
- anyticput = 1;
-
- /*{{{ calc internal and user co-ords */
- if (!log_array[axis]) {
- internal = datatype[axis] == TIME ? time_tic_just(timelevel[axis], tic) : tic;
- user = CheckZero(internal, step);
- } else {
- /* log scale => dont need to worry about zero ? */
- internal = tic;
- user = pow(base_array[axis], internal);
- }
- /*}}} */
- if (internal > internal_max)
- break; /* gone too far - end of series = VERYLARGE perhaps */
- if (internal >= internal_min) {
- /* continue; *//* maybe minitics!!!. user series starts below min ? */
-
- /*{{{ draw tick via callback */
- switch (def->type) {
- case TIC_DAY:{
- int d = (long) floor(user + 0.5) % 7;
- if (d < 0)
- d += 7;
- (*callback) (axis, internal, abbrev_day_names[d], lgrd);
- break;
- }
- case TIC_MONTH:{
- int m = (long) floor(user - 1) % 12;
- if (m < 0)
- m += 12;
- (*callback) (axis, internal, abbrev_month_names[m], lgrd);
- break;
- }
- default:{ /* comp or series */
- char label[64];
- if (datatype[axis] == TIME) {
- /* If they are doing polar time plot, good luck to them */
- gstrftime(label, 24, ticfmt[axis], (double) user);
- } else if (polar) {
- /* if rmin is set, we stored internally with r-rmin */
- /* Igor's polar-grid patch */
- #if 1
- /* HBB 990327: reverted to 'pre-Igor' version... */
- double r = fabs(user) + (autoscale_r & 1 ? 0 : rmin);
- #else
- /* Igor removed fabs to allow -ve labels */
- double r = user + (autoscale_r & 1 ? 0 : rmin);
- #endif
- gprintf(label, sizeof(label), ticfmt[axis], log_base, r);
- } else {
- gprintf(label, sizeof(label), ticfmt[axis], log_base, user);
- }
- (*callback) (axis, internal, label, lgrd);
- }
- }
- /*}}} */
-
- }
- if (minitics) {
- /*{{{ process minitics */
- double mplace, mtic;
- for (mplace = ministart; mplace < miniend; mplace += ministep) {
- if (datatype[axis] == TIME)
- mtic = time_tic_just(timelevel[axis] - 1, internal + mplace);
- else
- mtic = internal + (log_array[axis] && step <= 1.5 ? log(mplace) / log_base_array[axis] : mplace);
- if (inrange(mtic, internal_min, internal_max) &&
- inrange(mtic, start - step * SIGNIF, end + step * SIGNIF))
- (*callback) (axis, mtic, NULL, mgrd);
- }
- /*}}} */
- }
- }
- }
- }
- /*}}} */
-
- /*{{{ map_position */
- static void map_position(pos, x, y, what)
- struct position *pos;
- unsigned int *x, *y;
- char *what;
- {
- switch (pos->scalex) {
- case first_axes:
- {
- double xx = LogScale(pos->x, log_array[FIRST_X_AXIS], log_base_array[FIRST_X_AXIS], what, "x");
- *x = xleft + (xx - min_array[FIRST_X_AXIS]) * scale[FIRST_X_AXIS] + 0.5;
- break;
- }
- case second_axes:
- {
- double xx = LogScale(pos->x, log_array[SECOND_X_AXIS], log_base_array[SECOND_X_AXIS], what, "x");
- *x = xleft + (xx - min_array[SECOND_X_AXIS]) * scale[SECOND_X_AXIS] + 0.5;
- break;
- }
- case graph:
- {
- *x = xleft + pos->x * (xright - xleft) + 0.5;
- break;
- }
- case screen:
- {
- register struct termentry *t = term;
- *x = pos->x * (t->xmax) + 0.5;
- break;
- }
- }
- switch (pos->scaley) {
- case first_axes:
- {
- double yy = LogScale(pos->y, log_array[FIRST_Y_AXIS], log_base_array[FIRST_Y_AXIS], what, "y");
- *y = ybot + (yy - min_array[FIRST_Y_AXIS]) * scale[FIRST_Y_AXIS] + 0.5;
- return;
- }
- case second_axes:
- {
- double yy = LogScale(pos->y, log_array[SECOND_Y_AXIS], log_base_array[SECOND_Y_AXIS], what, "y");
- *y = ybot + (yy - min_array[SECOND_Y_AXIS]) * scale[SECOND_Y_AXIS] + 0.5;
- return;
- }
- case graph:
- {
- *y = ybot + pos->y * (ytop - ybot) + 0.5;
- return;
- }
- case screen:
- {
- register struct termentry *t = term;
- *y = pos->y * (t->ymax) + 0.5;
- return;
- }
- }
- }
- /*}}} */
-